OSDN Git Service

Support mDNS LAN peer discover (#1687)
authoryahtoo <yahtoo.ma@gmail.com>
Mon, 15 Apr 2019 08:57:09 +0000 (16:57 +0800)
committerPaladz <yzhu101@uottawa.ca>
Mon, 15 Apr 2019 08:57:09 +0000 (16:57 +0800)
* mv kad discover module to dht

* Add mdns function

* Add lan peer discover func

* Del unused files

* Add lib

* add test info

* Add debug info

* opz code format

* Add LAN peer field

* Del unused files

* Fix lib dependency error

* Change bestPeer logic preferred LAN peer

* Fix test file error

* Del unused file

* Del unused files

* del debug print

* Fix review bug

* change zeroconf lib directory

github.com/zeroconf->github.com/grandcat/zeroconf

* Fix review bugs

* Fix review bugs

* Fix review bugs

107 files changed:
crypto/ed25519/ed25519.go
netsync/peer.go
netsync/tool_test.go
p2p/defaults.go [new file with mode: 0644]
p2p/discover/dht/database.go [moved from p2p/discover/database.go with 99% similarity]
p2p/discover/dht/database_test.go [moved from p2p/discover/database_test.go with 99% similarity]
p2p/discover/dht/dns_seeds.go [moved from p2p/discover/dns_seeds.go with 98% similarity]
p2p/discover/dht/dns_seeds_test.go [moved from p2p/discover/dns_seeds_test.go with 99% similarity]
p2p/discover/dht/net.go [moved from p2p/discover/net.go with 99% similarity]
p2p/discover/dht/node.go [moved from p2p/discover/node.go with 99% similarity]
p2p/discover/dht/nodeevent_string.go [moved from p2p/discover/nodeevent_string.go with 97% similarity]
p2p/discover/dht/ntp.go [moved from p2p/discover/ntp.go with 99% similarity]
p2p/discover/dht/sim_run_test.go [moved from p2p/discover/sim_run_test.go with 99% similarity]
p2p/discover/dht/sim_testmain_test.go [moved from p2p/discover/sim_testmain_test.go with 98% similarity]
p2p/discover/dht/table.go [moved from p2p/discover/table.go with 99% similarity]
p2p/discover/dht/ticket.go [moved from p2p/discover/ticket.go with 99% similarity]
p2p/discover/dht/topic.go [moved from p2p/discover/topic.go with 99% similarity]
p2p/discover/dht/udp.go [moved from p2p/discover/udp.go with 99% similarity]
p2p/discover/mdns/lan_discover.go [new file with mode: 0644]
p2p/discover/mdns/lan_discover_test.go [new file with mode: 0644]
p2p/discover/mdns/mdns.go [new file with mode: 0644]
p2p/netaddress.go
p2p/peer.go
p2p/peer_test.go
p2p/switch.go
p2p/switch_test.go
p2p/test_util.go
vendor/github.com/cenkalti/backoff/LICENSE [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/README.md [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/backoff.go [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/context.go [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/exponential.go [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/retry.go [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/ticker.go [new file with mode: 0644]
vendor/github.com/cenkalti/backoff/tries.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/LICENSE [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/README.md [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/client.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/connection.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/doc.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/proxyservice/.gitignore [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/proxyservice/server.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/register/.gitignore [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/register/server.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/resolv/.gitignore [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/resolv/README.md [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/examples/resolv/client.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/server.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/service.go [new file with mode: 0644]
vendor/github.com/grandcat/zeroconf/utils.go [new file with mode: 0644]
vendor/github.com/miekg/dns/AUTHORS [new file with mode: 0644]
vendor/github.com/miekg/dns/CONTRIBUTORS [new file with mode: 0644]
vendor/github.com/miekg/dns/COPYRIGHT [new file with mode: 0644]
vendor/github.com/miekg/dns/Gopkg.lock [new file with mode: 0644]
vendor/github.com/miekg/dns/Gopkg.toml [new file with mode: 0644]
vendor/github.com/miekg/dns/LICENSE [new file with mode: 0644]
vendor/github.com/miekg/dns/Makefile.fuzz [new file with mode: 0644]
vendor/github.com/miekg/dns/Makefile.release [new file with mode: 0644]
vendor/github.com/miekg/dns/README.md [new file with mode: 0644]
vendor/github.com/miekg/dns/acceptfunc.go [new file with mode: 0644]
vendor/github.com/miekg/dns/client.go [new file with mode: 0644]
vendor/github.com/miekg/dns/clientconfig.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dane.go [new file with mode: 0644]
vendor/github.com/miekg/dns/defaults.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dns.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dnssec.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dnssec_keygen.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dnssec_keyscan.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dnssec_privkey.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dnsutil/util.go [new file with mode: 0644]
vendor/github.com/miekg/dns/dnsutil/util_test.go [new file with mode: 0644]
vendor/github.com/miekg/dns/doc.go [new file with mode: 0644]
vendor/github.com/miekg/dns/duplicate.go [new file with mode: 0644]
vendor/github.com/miekg/dns/duplicate_generate.go [new file with mode: 0644]
vendor/github.com/miekg/dns/edns.go [new file with mode: 0644]
vendor/github.com/miekg/dns/format.go [new file with mode: 0644]
vendor/github.com/miekg/dns/fuzz.go [new file with mode: 0644]
vendor/github.com/miekg/dns/generate.go [new file with mode: 0644]
vendor/github.com/miekg/dns/labels.go [new file with mode: 0644]
vendor/github.com/miekg/dns/listen_go111.go [new file with mode: 0644]
vendor/github.com/miekg/dns/listen_go_not111.go [new file with mode: 0644]
vendor/github.com/miekg/dns/msg.go [new file with mode: 0644]
vendor/github.com/miekg/dns/msg_generate.go [new file with mode: 0644]
vendor/github.com/miekg/dns/msg_helpers.go [new file with mode: 0644]
vendor/github.com/miekg/dns/nsecx.go [new file with mode: 0644]
vendor/github.com/miekg/dns/privaterr.go [new file with mode: 0644]
vendor/github.com/miekg/dns/reverse.go [new file with mode: 0644]
vendor/github.com/miekg/dns/sanitize.go [new file with mode: 0644]
vendor/github.com/miekg/dns/scan.go [new file with mode: 0644]
vendor/github.com/miekg/dns/scan_rr.go [new file with mode: 0644]
vendor/github.com/miekg/dns/serve_mux.go [new file with mode: 0644]
vendor/github.com/miekg/dns/server.go [new file with mode: 0644]
vendor/github.com/miekg/dns/sig0.go [new file with mode: 0644]
vendor/github.com/miekg/dns/singleinflight.go [new file with mode: 0644]
vendor/github.com/miekg/dns/smimea.go [new file with mode: 0644]
vendor/github.com/miekg/dns/tlsa.go [new file with mode: 0644]
vendor/github.com/miekg/dns/tsig.go [new file with mode: 0644]
vendor/github.com/miekg/dns/types.go [new file with mode: 0644]
vendor/github.com/miekg/dns/types_generate.go [new file with mode: 0644]
vendor/github.com/miekg/dns/udp.go [new file with mode: 0644]
vendor/github.com/miekg/dns/udp_windows.go [new file with mode: 0644]
vendor/github.com/miekg/dns/update.go [new file with mode: 0644]
vendor/github.com/miekg/dns/version.go [new file with mode: 0644]
vendor/github.com/miekg/dns/xfr.go [new file with mode: 0644]
vendor/github.com/miekg/dns/zduplicate.go [new file with mode: 0644]
vendor/github.com/miekg/dns/zmsg.go [new file with mode: 0644]
vendor/github.com/miekg/dns/ztypes.go [new file with mode: 0644]

index c46a306..a41d10a 100644 (file)
@@ -26,6 +26,8 @@ const (
        PrivateKeySize = 64
        // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
        SignatureSize = 64
+       // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
+       SeedSize = 32
 )
 
 // PublicKey is the type of Ed25519 public keys.
@@ -41,6 +43,15 @@ func (priv PrivateKey) Public() PublicKey {
        return PublicKey(publicKey)
 }
 
+// Seed returns the private key seed corresponding to priv. It is provided for
+// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
+// in this package.
+func (priv PrivateKey) Seed() []byte {
+       seed := make([]byte, SeedSize)
+       copy(seed, priv[:32])
+       return seed
+}
+
 func (priv PrivateKey) String() string {
        return hex.EncodeToString(priv)
 }
@@ -77,6 +88,34 @@ func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, er
        return publicKey, privateKey, nil
 }
 
+// NewKeyFromSeed calculates a private key from a seed. It will panic if
+// len(seed) is not SeedSize. This function is provided for interoperability
+// with RFC 8032. RFC 8032's private keys correspond to seeds in this
+// package.
+func NewKeyFromSeed(seed []byte) PrivateKey {
+       if l := len(seed); l != SeedSize {
+               panic("ed25519: bad seed length: " + strconv.Itoa(l))
+       }
+
+       digest := sha512.Sum512(seed)
+       digest[0] &= 248
+       digest[31] &= 127
+       digest[31] |= 64
+
+       var A edwards25519.ExtendedGroupElement
+       var hBytes [32]byte
+       copy(hBytes[:], digest[:])
+       edwards25519.GeScalarMultBase(&A, &hBytes)
+       var publicKeyBytes [32]byte
+       A.ToBytes(&publicKeyBytes)
+
+       privateKey := make([]byte, PrivateKeySize)
+       copy(privateKey, seed)
+       copy(privateKey[32:], publicKeyBytes[:])
+
+       return privateKey
+}
+
 // Sign signs the message with privateKey and returns a signature. It will
 // panic if len(privateKey) is not PrivateKeySize.
 func Sign(privateKey PrivateKey, message []byte) []byte {
index 5f33616..6a9f57b 100644 (file)
@@ -30,6 +30,7 @@ type BasePeer interface {
        ServiceFlag() consensus.ServiceFlag
        TrafficStatus() (*flowrate.Status, *flowrate.Status)
        TrySend(byte, interface{}) bool
+       IsLAN() bool
 }
 
 //BasePeerSet is the intergace for connection level peer manager
@@ -367,7 +368,7 @@ func (ps *peerSet) bestPeer(flag consensus.ServiceFlag) *peer {
                if !p.services.IsEnable(flag) {
                        continue
                }
-               if bestPeer == nil || p.height > bestPeer.height {
+               if bestPeer == nil || p.height > bestPeer.height || (p.height == bestPeer.height && p.IsLAN()) {
                        bestPeer = p
                }
        }
index 90575e6..bef9736 100644 (file)
@@ -44,6 +44,10 @@ func (p *P2PPeer) ID() string {
        return p.id
 }
 
+func (p *P2PPeer) IsLAN() bool {
+       return false
+}
+
 func (p *P2PPeer) ServiceFlag() consensus.ServiceFlag {
        return p.flag
 }
diff --git a/p2p/defaults.go b/p2p/defaults.go
new file mode 100644 (file)
index 0000000..df4ddd3
--- /dev/null
@@ -0,0 +1 @@
+package p2p
similarity index 99%
rename from p2p/discover/database.go
rename to p2p/discover/dht/database.go
index 9d33d1c..6714ed3 100644 (file)
@@ -1,7 +1,7 @@
 // Contains the node database, storing previously seen nodes and any collected
 // metadata about them for QoS purposes.
 
-package discover
+package dht
 
 import (
        "bytes"
similarity index 99%
rename from p2p/discover/database_test.go
rename to p2p/discover/dht/database_test.go
index b98bc1c..78dbe3c 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "bytes"
similarity index 98%
rename from p2p/discover/dns_seeds.go
rename to p2p/discover/dht/dns_seeds.go
index 235f29f..78468d0 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "net"
similarity index 99%
rename from p2p/discover/dns_seeds_test.go
rename to p2p/discover/dht/dns_seeds_test.go
index 3a31a10..694146f 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "reflect"
similarity index 99%
rename from p2p/discover/net.go
rename to p2p/discover/dht/net.go
index dcc071b..fd1204f 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "bytes"
similarity index 99%
rename from p2p/discover/node.go
rename to p2p/discover/dht/node.go
index e4f65ff..40d31cd 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "crypto/ecdsa"
similarity index 97%
rename from p2p/discover/nodeevent_string.go
rename to p2p/discover/dht/nodeevent_string.go
index 1bb7530..9bad77d 100644 (file)
@@ -1,6 +1,6 @@
 // Code generated by "stringer -type=nodeEvent"; DO NOT EDIT.
 
-package discover
+package dht
 
 import "strconv"
 
similarity index 99%
rename from p2p/discover/ntp.go
rename to p2p/discover/dht/ntp.go
index 1064bc1..c3354ad 100644 (file)
@@ -1,7 +1,7 @@
 // Contains the NTP time drift detection via the SNTP protocol:
 //   https://tools.ietf.org/html/rfc4330
 
-package discover
+package dht
 
 import (
        "fmt"
similarity index 99%
rename from p2p/discover/sim_run_test.go
rename to p2p/discover/dht/sim_run_test.go
index a2aef65..fd08405 100644 (file)
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
-package discover
+package dht
 
 import (
        "bufio"
similarity index 98%
rename from p2p/discover/sim_testmain_test.go
rename to p2p/discover/dht/sim_testmain_test.go
index ee71ff6..02ce021 100644 (file)
@@ -16,7 +16,7 @@
 
 // +build go1.4,nacl,faketime_simulation
 
-package discover
+package dht
 
 import (
        "os"
similarity index 99%
rename from p2p/discover/table.go
rename to p2p/discover/dht/table.go
index 62a00e6..c0d4645 100644 (file)
@@ -4,7 +4,7 @@
 // can be connected to. It uses a Kademlia-like protocol to maintain a
 // distributed database of the IDs and endpoints of all listening
 // nodes.
-package discover
+package dht
 
 import (
        "crypto/rand"
similarity index 99%
rename from p2p/discover/ticket.go
rename to p2p/discover/dht/ticket.go
index 10de037..9d842cf 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "bytes"
similarity index 99%
rename from p2p/discover/topic.go
rename to p2p/discover/dht/topic.go
index ef2d8a6..5390fbc 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "container/heap"
similarity index 99%
rename from p2p/discover/udp.go
rename to p2p/discover/dht/udp.go
index c0372d7..a702c82 100644 (file)
@@ -1,4 +1,4 @@
-package discover
+package dht
 
 import (
        "bytes"
diff --git a/p2p/discover/mdns/lan_discover.go b/p2p/discover/mdns/lan_discover.go
new file mode 100644 (file)
index 0000000..e9a81ba
--- /dev/null
@@ -0,0 +1,122 @@
+package mdns
+
+import (
+       "net"
+       "sync/atomic"
+       "time"
+
+       log "github.com/sirupsen/logrus"
+
+       "github.com/bytom/event"
+)
+
+const (
+       logModule            = "p2p/mdns"
+       registerServiceCycle = 10 * time.Minute
+       registerServiceDelay = 2 * time.Second
+)
+
+// LANPeerEvent represent LAN peer ip and port.
+type LANPeerEvent struct {
+       IP   []net.IP
+       Port int
+}
+
+// mDNSProtocol mdns protocol interface.
+type mDNSProtocol interface {
+       registerService(port int) error
+       registerResolver(event chan LANPeerEvent) error
+       stopService()
+       stopResolver()
+}
+
+// LANDiscover responsible for finding the related services registered LAN nodes.
+type LANDiscover struct {
+       protocol        mDNSProtocol
+       resolving       uint32
+       servicePort     int //service port
+       entries         chan LANPeerEvent
+       eventDispatcher *event.Dispatcher
+       quite           chan struct{}
+}
+
+// NewLANDiscover create a new LAN node discover.
+func NewLANDiscover(protocol mDNSProtocol, port int) *LANDiscover {
+       ld := &LANDiscover{
+               protocol:        protocol,
+               servicePort:     port,
+               entries:         make(chan LANPeerEvent, 1024),
+               eventDispatcher: event.NewDispatcher(),
+               quite:           make(chan struct{}),
+       }
+       // register service
+       go ld.registerServiceRoutine()
+       go ld.getLANPeerLoop()
+       return ld
+}
+
+// Stop stop LAN discover.
+func (ld *LANDiscover) Stop() {
+       close(ld.quite)
+       ld.protocol.stopResolver()
+       ld.protocol.stopService()
+       ld.eventDispatcher.Stop()
+}
+
+// Subscribe used to subscribe for LANPeerEvent.
+func (ld *LANDiscover) Subscribe() (*event.Subscription, error) {
+       //subscribe LANPeerEvent.
+       sub, err := ld.eventDispatcher.Subscribe(LANPeerEvent{})
+       if err != nil {
+               return nil, err
+       }
+
+       //need to register the parser once.
+       if atomic.CompareAndSwapUint32(&ld.resolving, 0, 1) {
+               if err = ld.protocol.registerResolver(ld.entries); err != nil {
+                       return nil, err
+               }
+       }
+
+       return sub, nil
+}
+
+// register service routine, will be re-registered periodically
+// for the stability of node discovery.
+func (ld *LANDiscover) registerServiceRoutine() {
+       time.Sleep(registerServiceDelay)
+       if err := ld.protocol.registerService(ld.servicePort); err != nil {
+               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("mdns service register error")
+               return
+       }
+
+       ticker := time.NewTicker(registerServiceCycle)
+       defer ticker.Stop()
+       for {
+               select {
+               case <-ticker.C:
+                       ld.protocol.stopService()
+                       if err := ld.protocol.registerService(ld.servicePort); err != nil {
+                               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("mdns service register error")
+                               return
+                       }
+               case <-ld.quite:
+                       return
+               }
+       }
+}
+
+// obtain the lan peer event from the specific protocol
+// and distribute it to the subscriber.
+func (ld *LANDiscover) getLANPeerLoop() {
+       for {
+               select {
+               case entry := <-ld.entries:
+                       if err := ld.eventDispatcher.Post(entry); err != nil {
+                               log.WithFields(log.Fields{"module": logModule, "err": err}).Error("event dispatch error")
+                       }
+               case <-ld.quite:
+                       return
+               }
+       }
+}
diff --git a/p2p/discover/mdns/lan_discover_test.go b/p2p/discover/mdns/lan_discover_test.go
new file mode 100644 (file)
index 0000000..9b55d53
--- /dev/null
@@ -0,0 +1,77 @@
+package mdns
+
+import (
+       "testing"
+
+       "github.com/davecgh/go-spew/spew"
+       "net"
+       "reflect"
+       "time"
+)
+
+var wantEvents = []LANPeerEvent{
+       {IP: []net.IP{net.IPv4(1, 2, 3, 4)}, Port: 1024},
+       {IP: []net.IP{net.IPv4(1, 2, 3, 4), net.IPv4(5, 6, 7, 8)}, Port: 1024},
+}
+
+type mockProtocol struct {
+       entries chan *LANPeerEvent
+}
+
+func newMockProtocol() *mockProtocol {
+       return &mockProtocol{
+               entries: make(chan *LANPeerEvent, 1024),
+       }
+}
+func (m *mockProtocol) registerService(port int) error {
+       return nil
+}
+
+func (m *mockProtocol) registerResolver(event chan LANPeerEvent) error {
+       for _, peerEvent := range wantEvents {
+               event <- peerEvent
+       }
+       return nil
+}
+
+func (m *mockProtocol) stopService() {
+
+}
+
+func (m *mockProtocol) stopResolver() {
+
+}
+
+func TestLanDiscover(t *testing.T) {
+       lanDiscv := NewLANDiscover(newMockProtocol(), 12345)
+       defer lanDiscv.Stop()
+
+       lanPeerMsgSub, err := lanDiscv.Subscribe()
+       if err != nil {
+               t.Fatal("subscribe lan peer msg err")
+       }
+
+       var gotevents = []LANPeerEvent{}
+       timeout := time.After(1 * time.Second)
+       for {
+               select {
+               case obj, ok := <-lanPeerMsgSub.Chan():
+                       if !ok {
+                               t.Fatal("subscription channel closed")
+                               return
+                       }
+
+                       ev, ok := obj.Data.(LANPeerEvent)
+                       if !ok {
+                               t.Fatal("event type error")
+                               continue
+                       }
+                       gotevents = append(gotevents, ev)
+               case <-timeout:
+                       if !reflect.DeepEqual(gotevents, wantEvents) {
+                               t.Fatalf("mismatch for test lan discover got %s want %s", spew.Sdump(gotevents), spew.Sdump(wantEvents))
+                       }
+                       return
+               }
+       }
+}
diff --git a/p2p/discover/mdns/mdns.go b/p2p/discover/mdns/mdns.go
new file mode 100644 (file)
index 0000000..c061be9
--- /dev/null
@@ -0,0 +1,66 @@
+package mdns
+
+import (
+       "context"
+
+       "github.com/grandcat/zeroconf"
+)
+
+const (
+       instanceName = "bytomd"
+       serviceName  = "lanDiscover"
+       domainName   = "local"
+)
+
+// Protocol decoration ZeroConf,which is a pure Golang library
+// that employs Multicast DNS-SD.
+type Protocol struct {
+       entries chan *zeroconf.ServiceEntry
+       server  *zeroconf.Server
+       quite   chan struct{}
+}
+
+// NewProtocol create a specific Protocol.
+func NewProtocol() *Protocol {
+       return &Protocol{
+               entries: make(chan *zeroconf.ServiceEntry),
+               quite:   make(chan struct{}),
+       }
+}
+
+func (m *Protocol) getLanPeerLoop(event chan LANPeerEvent) {
+       for {
+               select {
+               case entry := <-m.entries:
+                       event <- LANPeerEvent{IP: entry.AddrIPv4, Port: entry.Port}
+               case <-m.quite:
+                       return
+               }
+       }
+}
+
+func (m *Protocol) registerService(port int) error {
+       var err error
+       m.server, err = zeroconf.Register(instanceName, serviceName, domainName, port, nil, nil)
+       return err
+}
+
+func (m *Protocol) registerResolver(event chan LANPeerEvent) error {
+       go m.getLanPeerLoop(event)
+       resolver, err := zeroconf.NewResolver(nil)
+       if err != nil {
+               return err
+       }
+
+       return resolver.Browse(context.Background(), serviceName, domainName, m.entries)
+}
+
+func (m *Protocol) stopResolver() {
+       close(m.quite)
+}
+
+func (m *Protocol) stopService() {
+       if m.server != nil {
+               m.server.Shutdown()
+       }
+}
index 97a5d0e..4b870fb 100644 (file)
@@ -18,9 +18,10 @@ import (
 // NetAddress defines information about a peer on the network
 // including its IP address, and port.
 type NetAddress struct {
-       IP   net.IP
-       Port uint16
-       str  string
+       IP    net.IP
+       Port  uint16
+       str   string
+       isLAN bool
 }
 
 // NewNetAddress returns a new NetAddress using the provided TCP
@@ -98,6 +99,20 @@ func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
        }
 }
 
+// NewLANNetAddressIPPort returns a new LAN NetAddress using the provided IP
+// and port number.
+func NewLANNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
+       return &NetAddress{
+               IP:   ip,
+               Port: port,
+               str: net.JoinHostPort(
+                       ip.String(),
+                       strconv.FormatUint(uint64(port), 10),
+               ),
+               isLAN: true,
+       }
+}
+
 // Equals reports whether na and other are the same addresses.
 func (na *NetAddress) Equals(other interface{}) bool {
        if o, ok := other.(*NetAddress); ok {
index 87c159a..01858c5 100644 (file)
@@ -56,6 +56,7 @@ type Peer struct {
        *peerConn
        mconn *connection.MConnection // multiplex connection
        Key   string
+       isLAN bool
 }
 
 // OnStart implements BaseService.
@@ -71,12 +72,13 @@ func (p *Peer) OnStop() {
        p.mconn.Stop()
 }
 
-func newPeer(pc *peerConn, nodeInfo *NodeInfo, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{})) *Peer {
+func newPeer(pc *peerConn, nodeInfo *NodeInfo, reactorsByCh map[byte]Reactor, chDescs []*connection.ChannelDescriptor, onPeerError func(*Peer, interface{}), isLAN bool) *Peer {
        // Key and NodeInfo are set after Handshake
        p := &Peer{
                peerConn: pc,
                NodeInfo: nodeInfo,
                Key:      nodeInfo.PubKey.KeyString(),
+               isLAN:    isLAN,
        }
        p.mconn = createMConnection(pc.conn, p, reactorsByCh, chDescs, onPeerError, pc.config.MConfig)
        p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
@@ -183,6 +185,11 @@ func (p *Peer) IsOutbound() bool {
        return p.outbound
 }
 
+// IsLAN returns true if peer is LAN peer, false otherwise.
+func (p *Peer) IsLAN() bool {
+       return p.isLAN
+}
+
 // PubKey returns peer's public key.
 func (p *Peer) PubKey() crypto.PubKeyEd25519 {
        return p.conn.(*connection.SecretConnection).RemotePubKey()
index 4e34adb..74b9744 100644 (file)
@@ -83,7 +83,7 @@ func createOutboundPeerAndPerformHandshake(
                fmt.Println(err)
                return nil, err
        }
-       p := newPeer(pc, nodeInfo, reactorsByCh, chDescs, nil)
+       p := newPeer(pc, nodeInfo, reactorsByCh, chDescs, nil, false)
        return p, nil
 }
 
index e501d30..ecddbeb 100644 (file)
@@ -15,13 +15,15 @@ import (
        cfg "github.com/bytom/config"
        "github.com/bytom/consensus"
        "github.com/bytom/crypto/ed25519"
+       dbm "github.com/bytom/database/leveldb"
        "github.com/bytom/errors"
+       "github.com/bytom/event"
        "github.com/bytom/p2p/connection"
-       "github.com/bytom/p2p/discover"
+       "github.com/bytom/p2p/discover/dht"
+       "github.com/bytom/p2p/discover/mdns"
        "github.com/bytom/p2p/netutil"
        "github.com/bytom/p2p/trust"
        "github.com/bytom/version"
-       dbm "github.com/bytom/database/leveldb"
 )
 
 const (
@@ -30,6 +32,7 @@ const (
        logModule          = "p2p"
 
        minNumOutboundPeers = 4
+       maxNumLANPeers      = 5
 )
 
 //pre-define errors for connecting fail
@@ -41,7 +44,12 @@ var (
 )
 
 type discv interface {
-       ReadRandomNodes(buf []*discover.Node) (n int)
+       ReadRandomNodes(buf []*dht.Node) (n int)
+}
+
+type lanDiscv interface {
+       Subscribe() (*event.Subscription, error)
+       Stop()
 }
 
 // Switch handles peer connections and exposes an API to receive incoming messages
@@ -62,6 +70,7 @@ type Switch struct {
        nodeInfo     *NodeInfo             // our node info
        nodePrivKey  crypto.PrivKeyEd25519 // our node privkey
        discv        discv
+       lanDiscv     lanDiscv
        bannedPeer   map[string]time.Time
        db           dbm.DB
        mtx          sync.Mutex
@@ -72,7 +81,8 @@ func NewSwitch(config *cfg.Config) (*Switch, error) {
        var err error
        var l Listener
        var listenAddr string
-       var discv *discover.Network
+       var discv *dht.Network
+       var lanDiscv *mdns.LANDiscover
 
        blacklistDB := dbm.NewDB("trusthistory", config.DBBackend, config.DBDir())
        config.P2P.PrivateKey, err = config.NodeKey()
@@ -91,17 +101,19 @@ func NewSwitch(config *cfg.Config) (*Switch, error) {
        if !config.VaultMode {
                // Create listener
                l, listenAddr = GetListener(config.P2P)
-               discv, err = discover.NewDiscover(config, ed25519.PrivateKey(bytes), l.ExternalAddress().Port)
+               discv, err = dht.NewDiscover(config, ed25519.PrivateKey(bytes), l.ExternalAddress().Port)
                if err != nil {
                        return nil, err
                }
+
+               lanDiscv = mdns.NewLANDiscover(mdns.NewProtocol(), int(l.ExternalAddress().Port))
        }
 
-       return newSwitch(config, discv, blacklistDB, l, privKey, listenAddr)
+       return newSwitch(config, discv, lanDiscv, blacklistDB, l, privKey, listenAddr)
 }
 
 // newSwitch creates a new Switch with the given config.
-func newSwitch(config *cfg.Config, discv discv, blacklistDB dbm.DB, l Listener, priv crypto.PrivKeyEd25519, listenAddr string) (*Switch, error) {
+func newSwitch(config *cfg.Config, discv discv, lanDiscv lanDiscv, blacklistDB dbm.DB, l Listener, priv crypto.PrivKeyEd25519, listenAddr string) (*Switch, error) {
        sw := &Switch{
                Config:       config,
                peerConfig:   DefaultPeerConfig(config.P2P),
@@ -112,6 +124,7 @@ func newSwitch(config *cfg.Config, discv discv, blacklistDB dbm.DB, l Listener,
                dialing:      cmn.NewCMap(),
                nodePrivKey:  priv,
                discv:        discv,
+               lanDiscv:     lanDiscv,
                db:           blacklistDB,
                nodeInfo:     NewNodeInfo(config, priv.PubKey().Unwrap().(crypto.PubKeyEd25519), listenAddr),
                bannedPeer:   make(map[string]time.Time),
@@ -137,11 +150,16 @@ func (sw *Switch) OnStart() error {
                go sw.listenerRoutine(listener)
        }
        go sw.ensureOutboundPeersRoutine()
+       go sw.connectLANPeersRoutine()
+
        return nil
 }
 
 // OnStop implements BaseService. It stops all listeners, peers, and reactors.
 func (sw *Switch) OnStop() {
+       if sw.lanDiscv != nil {
+               sw.lanDiscv.Stop()
+       }
        for _, listener := range sw.listeners {
                listener.Stop()
        }
@@ -177,7 +195,7 @@ func (sw *Switch) AddBannedPeer(ip string) error {
 // it starts the peer and adds it to the switch.
 // NOTE: This performs a blocking handshake before the peer is added.
 // CONTRACT: If error is returned, peer is nil, and conn is immediately closed.
-func (sw *Switch) AddPeer(pc *peerConn) error {
+func (sw *Switch) AddPeer(pc *peerConn, isLAN bool) error {
        peerNodeInfo, err := pc.HandshakeTimeout(sw.nodeInfo, sw.peerConfig.HandshakeTimeout)
        if err != nil {
                return err
@@ -190,7 +208,7 @@ func (sw *Switch) AddPeer(pc *peerConn) error {
                return err
        }
 
-       peer := newPeer(pc, peerNodeInfo, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError)
+       peer := newPeer(pc, peerNodeInfo, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, isLAN)
        if err := sw.filterConnByPeer(peer); err != nil {
                return err
        }
@@ -248,7 +266,7 @@ func (sw *Switch) DialPeerWithAddress(addr *NetAddress) error {
                return err
        }
 
-       if err = sw.AddPeer(pc); err != nil {
+       if err = sw.AddPeer(pc, addr.isLAN); err != nil {
                log.WithFields(log.Fields{"module": logModule, "address": addr, " err": err}).Error("DialPeer fail on switch AddPeer")
                pc.CloseConn()
                return err
@@ -286,14 +304,17 @@ func (sw *Switch) Listeners() []Listener {
 }
 
 // NumPeers Returns the count of outbound/inbound and outbound-dialing peers.
-func (sw *Switch) NumPeers() (outbound, inbound, dialing int) {
+func (sw *Switch) NumPeers() (lan, outbound, inbound, dialing int) {
        peers := sw.peers.List()
        for _, peer := range peers {
-               if peer.outbound {
+               if peer.outbound && !peer.isLAN {
                        outbound++
                } else {
                        inbound++
                }
+               if peer.isLAN {
+                       lan++
+               }
        }
        dialing = sw.dialing.Size()
        return
@@ -332,7 +353,7 @@ func (sw *Switch) addPeerWithConnection(conn net.Conn) error {
                return err
        }
 
-       if err = sw.AddPeer(peerConn); err != nil {
+       if err = sw.AddPeer(peerConn, false); err != nil {
                if err := conn.Close(); err != nil {
                        log.WithFields(log.Fields{"module": logModule, "remote peer:": conn.RemoteAddr().String(), " err:": err}).Error("closes connection err")
                }
@@ -359,6 +380,50 @@ func (sw *Switch) checkBannedPeer(peer string) error {
        return nil
 }
 
+func (sw *Switch) connectLANPeers(lanPeer mdns.LANPeerEvent) {
+       lanPeers, _, _, numDialing := sw.NumPeers()
+       numToDial := maxNumLANPeers - lanPeers
+       log.WithFields(log.Fields{"module": logModule, "numDialing": numDialing, "numToDial": numToDial}).Debug("connect LAN peers")
+       if numToDial <= 0 {
+               return
+       }
+       addresses := make([]*NetAddress, 0)
+       for i := 0; i < len(lanPeer.IP); i++ {
+               addresses = append(addresses, NewLANNetAddressIPPort(lanPeer.IP[i], uint16(lanPeer.Port)))
+       }
+       sw.dialPeers(addresses)
+}
+
+func (sw *Switch) connectLANPeersRoutine() {
+       if sw.lanDiscv == nil {
+               return
+       }
+
+       lanPeerEventSub, err := sw.lanDiscv.Subscribe()
+       if err != nil {
+               log.WithFields(log.Fields{"module": logModule, "err": err}).Warning("subscribe LAN Peer Event error")
+               return
+       }
+
+       for {
+               select {
+               case obj, ok := <-lanPeerEventSub.Chan():
+                       if !ok {
+                               log.WithFields(log.Fields{"module": logModule}).Warning("LAN peer event subscription channel closed")
+                               return
+                       }
+                       LANPeer, ok := obj.Data.(mdns.LANPeerEvent)
+                       if !ok {
+                               log.WithFields(log.Fields{"module": logModule}).Error("event type error")
+                               continue
+                       }
+                       sw.connectLANPeers(LANPeer)
+               case <-sw.Quit:
+                       return
+               }
+       }
+}
+
 func (sw *Switch) delBannedPeer(addr string) error {
        sw.mtx.Lock()
        defer sw.mtx.Unlock()
@@ -426,69 +491,61 @@ func (sw *Switch) dialPeerWorker(a *NetAddress, wg *sync.WaitGroup) {
        wg.Done()
 }
 
-func (sw *Switch) ensureKeepConnectPeers() {
-       keepDials := netutil.CheckAndSplitAddresses(sw.Config.P2P.KeepDial)
+func (sw *Switch) dialPeers(addresses []*NetAddress) {
        connectedPeers := make(map[string]struct{})
        for _, peer := range sw.Peers().List() {
                connectedPeers[peer.remoteAddrHost()] = struct{}{}
        }
 
        var wg sync.WaitGroup
-       for _, keepDial := range keepDials {
-               try, err := NewNetAddressString(keepDial)
-               if err != nil {
-                       log.WithFields(log.Fields{"module": logModule, "err": err, "address": keepDial}).Warn("parse address to NetAddress")
-                       continue
-               }
-
-               if sw.NodeInfo().ListenAddr == try.String() {
+       for _, address := range addresses {
+               if sw.NodeInfo().ListenAddr == address.String() {
                        continue
                }
-               if dialling := sw.IsDialing(try); dialling {
+               if dialling := sw.IsDialing(address); dialling {
                        continue
                }
-               if _, ok := connectedPeers[try.IP.String()]; ok {
+               if _, ok := connectedPeers[address.IP.String()]; ok {
                        continue
                }
 
                wg.Add(1)
-               go sw.dialPeerWorker(try, &wg)
+               go sw.dialPeerWorker(address, &wg)
        }
        wg.Wait()
 }
 
+func (sw *Switch) ensureKeepConnectPeers() {
+       keepDials := netutil.CheckAndSplitAddresses(sw.Config.P2P.KeepDial)
+       addresses := make([]*NetAddress, 0)
+       for _, keepDial := range keepDials {
+               address, err := NewNetAddressString(keepDial)
+               if err != nil {
+                       log.WithFields(log.Fields{"module": logModule, "err": err, "address": keepDial}).Warn("parse address to NetAddress")
+                       continue
+               }
+               addresses = append(addresses, address)
+       }
+
+       sw.dialPeers(addresses)
+}
+
 func (sw *Switch) ensureOutboundPeers() {
-       numOutPeers, _, numDialing := sw.NumPeers()
-       numToDial := (minNumOutboundPeers - (numOutPeers + numDialing))
-       log.WithFields(log.Fields{"module": logModule, "numOutPeers": numOutPeers, "numDialing": numDialing, "numToDial": numToDial}).Debug("ensure peers")
+       lanPeers, numOutPeers, _, numDialing := sw.NumPeers()
+       numToDial := minNumOutboundPeers - (numOutPeers + numDialing)
+       log.WithFields(log.Fields{"module": logModule, "numOutPeers": numOutPeers, "LANPeers": lanPeers, "numDialing": numDialing, "numToDial": numToDial}).Debug("ensure peers")
        if numToDial <= 0 {
                return
        }
 
-       connectedPeers := make(map[string]struct{})
-       for _, peer := range sw.Peers().List() {
-               connectedPeers[peer.remoteAddrHost()] = struct{}{}
-       }
-
-       var wg sync.WaitGroup
-       nodes := make([]*discover.Node, numToDial)
+       nodes := make([]*dht.Node, numToDial)
        n := sw.discv.ReadRandomNodes(nodes)
+       addresses := make([]*NetAddress, 0)
        for i := 0; i < n; i++ {
-               try := NewNetAddressIPPort(nodes[i].IP, nodes[i].TCP)
-               if sw.NodeInfo().ListenAddr == try.String() {
-                       continue
-               }
-               if dialling := sw.IsDialing(try); dialling {
-                       continue
-               }
-               if _, ok := connectedPeers[try.IP.String()]; ok {
-                       continue
-               }
-
-               wg.Add(1)
-               go sw.dialPeerWorker(try, &wg)
+               address := NewNetAddressIPPort(nodes[i].IP, nodes[i].TCP)
+               addresses = append(addresses, address)
        }
-       wg.Wait()
+       sw.dialPeers(addresses)
 }
 
 func (sw *Switch) ensureOutboundPeersRoutine() {
index 8f39926..f91c0ef 100644 (file)
@@ -248,7 +248,7 @@ func TestDuplicateInBoundPeer(t *testing.T) {
        go inp1.dial(addr)
 
        time.Sleep(1 * time.Second)
-       if outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 1 {
+       if _, outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 1 {
                t.Fatal("TestDuplicateInBoundPeer peer size error want 1, got:", outbound, inbound, dialing, spew.Sdump(s1.peers.lookup))
        }
 }
@@ -299,7 +299,7 @@ func TestAddInboundPeer(t *testing.T) {
        go inp2.dial(addr)
 
        time.Sleep(1 * time.Second)
-       if outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 2 {
+       if _, outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 2 {
                t.Fatal("TestAddInboundPeer peer size error want 2 got:", spew.Sdump(s1.peers.lookup))
        }
 }
@@ -342,17 +342,17 @@ func TestStopPeer(t *testing.T) {
                t.Fatal(err)
        }
        time.Sleep(1 * time.Second)
-       if outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 2 {
+       if _, outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 2 {
                t.Fatal("TestStopPeer peer size error want 2,got:", spew.Sdump(s1.peers.lookup))
        }
 
        s1.StopPeerGracefully(s1.peers.list[0].Key)
-       if outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 1 {
+       if _, outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 1 {
                t.Fatal("TestStopPeer peer size error,want 1,got:", spew.Sdump(s1.peers.lookup))
        }
 
        s1.StopPeerForError(s1.peers.list[0], "stop for test")
-       if outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 0 {
+       if _, outbound, inbound, dialing := s1.NumPeers(); outbound+inbound+dialing != 0 {
                t.Fatal("TestStopPeer peer size error,want 0, got:", spew.Sdump(s1.peers.lookup))
        }
 }
index 00b172b..1d95a8b 100644 (file)
@@ -8,9 +8,9 @@ import (
        cmn "github.com/tendermint/tmlibs/common"
 
        cfg "github.com/bytom/config"
-       "github.com/bytom/p2p/connection"
-       "github.com/bytom/p2p/discover"
        dbm "github.com/bytom/database/leveldb"
+       "github.com/bytom/p2p/connection"
+       "github.com/bytom/p2p/discover/dht"
 )
 
 //PanicOnAddPeerErr add peer error
@@ -84,14 +84,14 @@ func startSwitches(switches []*Switch) error {
 type mockDiscv struct {
 }
 
-func (m *mockDiscv) ReadRandomNodes(buf []*discover.Node) (n int) {
+func (m *mockDiscv) ReadRandomNodes(buf []*dht.Node) (n int) {
        return 0
 }
 
 func MakeSwitch(cfg *cfg.Config, testdb dbm.DB, privKey crypto.PrivKeyEd25519, initSwitch func(*Switch) *Switch) *Switch {
        // new switch, add reactors
        l, listenAddr := GetListener(cfg.P2P)
-       sw, err := newSwitch(cfg, new(mockDiscv), testdb, l, privKey, listenAddr)
+       sw, err := newSwitch(cfg, new(mockDiscv), nil, testdb, l, privKey, listenAddr)
        if err != nil {
                log.Errorf("create switch error: %s", err)
                return nil
diff --git a/vendor/github.com/cenkalti/backoff/LICENSE b/vendor/github.com/cenkalti/backoff/LICENSE
new file mode 100644 (file)
index 0000000..89b8179
--- /dev/null
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Cenk Altı
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/cenkalti/backoff/README.md b/vendor/github.com/cenkalti/backoff/README.md
new file mode 100644 (file)
index 0000000..55ebc98
--- /dev/null
@@ -0,0 +1,30 @@
+# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
+
+This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
+
+[Exponential backoff][exponential backoff wiki]
+is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
+in order to gradually find an acceptable rate.
+The retries exponentially increase and stop increasing when a certain threshold is met.
+
+## Usage
+
+See https://godoc.org/github.com/cenkalti/backoff#pkg-examples
+
+## Contributing
+
+* I would like to keep this library as small as possible.
+* Please don't send a PR without opening an issue and discussing it first.
+* If proposed change is not a common use case, I will probably not accept it.
+
+[godoc]: https://godoc.org/github.com/cenkalti/backoff
+[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
+[travis]: https://travis-ci.org/cenkalti/backoff
+[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
+[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
+[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
+
+[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
+[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
+
+[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_
diff --git a/vendor/github.com/cenkalti/backoff/backoff.go b/vendor/github.com/cenkalti/backoff/backoff.go
new file mode 100644 (file)
index 0000000..3676ee4
--- /dev/null
@@ -0,0 +1,66 @@
+// Package backoff implements backoff algorithms for retrying operations.
+//
+// Use Retry function for retrying operations that may fail.
+// If Retry does not meet your needs,
+// copy/paste the function into your project and modify as you wish.
+//
+// There is also Ticker type similar to time.Ticker.
+// You can use it if you need to work with channels.
+//
+// See Examples section below for usage examples.
+package backoff
+
+import "time"
+
+// BackOff is a backoff policy for retrying an operation.
+type BackOff interface {
+       // NextBackOff returns the duration to wait before retrying the operation,
+       // or backoff. Stop to indicate that no more retries should be made.
+       //
+       // Example usage:
+       //
+       //      duration := backoff.NextBackOff();
+       //      if (duration == backoff.Stop) {
+       //              // Do not retry operation.
+       //      } else {
+       //              // Sleep for duration and retry operation.
+       //      }
+       //
+       NextBackOff() time.Duration
+
+       // Reset to initial state.
+       Reset()
+}
+
+// Stop indicates that no more retries should be made for use in NextBackOff().
+const Stop time.Duration = -1
+
+// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
+// meaning that the operation is retried immediately without waiting, indefinitely.
+type ZeroBackOff struct{}
+
+func (b *ZeroBackOff) Reset() {}
+
+func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
+
+// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
+// NextBackOff(), meaning that the operation should never be retried.
+type StopBackOff struct{}
+
+func (b *StopBackOff) Reset() {}
+
+func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
+
+// ConstantBackOff is a backoff policy that always returns the same backoff delay.
+// This is in contrast to an exponential backoff policy,
+// which returns a delay that grows longer as you call NextBackOff() over and over again.
+type ConstantBackOff struct {
+       Interval time.Duration
+}
+
+func (b *ConstantBackOff) Reset()                     {}
+func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
+
+func NewConstantBackOff(d time.Duration) *ConstantBackOff {
+       return &ConstantBackOff{Interval: d}
+}
diff --git a/vendor/github.com/cenkalti/backoff/context.go b/vendor/github.com/cenkalti/backoff/context.go
new file mode 100644 (file)
index 0000000..7706faa
--- /dev/null
@@ -0,0 +1,63 @@
+package backoff
+
+import (
+       "context"
+       "time"
+)
+
+// BackOffContext is a backoff policy that stops retrying after the context
+// is canceled.
+type BackOffContext interface {
+       BackOff
+       Context() context.Context
+}
+
+type backOffContext struct {
+       BackOff
+       ctx context.Context
+}
+
+// WithContext returns a BackOffContext with context ctx
+//
+// ctx must not be nil
+func WithContext(b BackOff, ctx context.Context) BackOffContext {
+       if ctx == nil {
+               panic("nil context")
+       }
+
+       if b, ok := b.(*backOffContext); ok {
+               return &backOffContext{
+                       BackOff: b.BackOff,
+                       ctx:     ctx,
+               }
+       }
+
+       return &backOffContext{
+               BackOff: b,
+               ctx:     ctx,
+       }
+}
+
+func ensureContext(b BackOff) BackOffContext {
+       if cb, ok := b.(BackOffContext); ok {
+               return cb
+       }
+       return WithContext(b, context.Background())
+}
+
+func (b *backOffContext) Context() context.Context {
+       return b.ctx
+}
+
+func (b *backOffContext) NextBackOff() time.Duration {
+       select {
+       case <-b.ctx.Done():
+               return Stop
+       default:
+       }
+       next := b.BackOff.NextBackOff()
+       if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next {
+               return Stop
+       }
+       return next
+}
diff --git a/vendor/github.com/cenkalti/backoff/exponential.go b/vendor/github.com/cenkalti/backoff/exponential.go
new file mode 100644 (file)
index 0000000..a031a65
--- /dev/null
@@ -0,0 +1,153 @@
+package backoff
+
+import (
+       "math/rand"
+       "time"
+)
+
+/*
+ExponentialBackOff is a backoff implementation that increases the backoff
+period for each retry attempt using a randomization function that grows exponentially.
+
+NextBackOff() is calculated using the following formula:
+
+ randomized interval =
+     RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
+
+In other words NextBackOff() will range between the randomization factor
+percentage below and above the retry interval.
+
+For example, given the following parameters:
+
+ RetryInterval = 2
+ RandomizationFactor = 0.5
+ Multiplier = 2
+
+the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
+multiplied by the exponential, that is, between 2 and 6 seconds.
+
+Note: MaxInterval caps the RetryInterval and not the randomized interval.
+
+If the time elapsed since an ExponentialBackOff instance is created goes past the
+MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
+
+The elapsed time can be reset by calling Reset().
+
+Example: Given the following default arguments, for 10 tries the sequence will be,
+and assuming we go over the MaxElapsedTime on the 10th try:
+
+ Request #  RetryInterval (seconds)  Randomized Interval (seconds)
+
+  1          0.5                     [0.25,   0.75]
+  2          0.75                    [0.375,  1.125]
+  3          1.125                   [0.562,  1.687]
+  4          1.687                   [0.8435, 2.53]
+  5          2.53                    [1.265,  3.795]
+  6          3.795                   [1.897,  5.692]
+  7          5.692                   [2.846,  8.538]
+  8          8.538                   [4.269, 12.807]
+  9         12.807                   [6.403, 19.210]
+ 10         19.210                   backoff.Stop
+
+Note: Implementation is not thread-safe.
+*/
+type ExponentialBackOff struct {
+       InitialInterval     time.Duration
+       RandomizationFactor float64
+       Multiplier          float64
+       MaxInterval         time.Duration
+       // After MaxElapsedTime the ExponentialBackOff stops.
+       // It never stops if MaxElapsedTime == 0.
+       MaxElapsedTime time.Duration
+       Clock          Clock
+
+       currentInterval time.Duration
+       startTime       time.Time
+}
+
+// Clock is an interface that returns current time for BackOff.
+type Clock interface {
+       Now() time.Time
+}
+
+// Default values for ExponentialBackOff.
+const (
+       DefaultInitialInterval     = 500 * time.Millisecond
+       DefaultRandomizationFactor = 0.5
+       DefaultMultiplier          = 1.5
+       DefaultMaxInterval         = 60 * time.Second
+       DefaultMaxElapsedTime      = 15 * time.Minute
+)
+
+// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
+func NewExponentialBackOff() *ExponentialBackOff {
+       b := &ExponentialBackOff{
+               InitialInterval:     DefaultInitialInterval,
+               RandomizationFactor: DefaultRandomizationFactor,
+               Multiplier:          DefaultMultiplier,
+               MaxInterval:         DefaultMaxInterval,
+               MaxElapsedTime:      DefaultMaxElapsedTime,
+               Clock:               SystemClock,
+       }
+       b.Reset()
+       return b
+}
+
+type systemClock struct{}
+
+func (t systemClock) Now() time.Time {
+       return time.Now()
+}
+
+// SystemClock implements Clock interface that uses time.Now().
+var SystemClock = systemClock{}
+
+// Reset the interval back to the initial retry interval and restarts the timer.
+func (b *ExponentialBackOff) Reset() {
+       b.currentInterval = b.InitialInterval
+       b.startTime = b.Clock.Now()
+}
+
+// NextBackOff calculates the next backoff interval using the formula:
+//     Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
+func (b *ExponentialBackOff) NextBackOff() time.Duration {
+       // Make sure we have not gone over the maximum elapsed time.
+       if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
+               return Stop
+       }
+       defer b.incrementCurrentInterval()
+       return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
+}
+
+// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
+// is created and is reset when Reset() is called.
+//
+// The elapsed time is computed using time.Now().UnixNano(). It is
+// safe to call even while the backoff policy is used by a running
+// ticker.
+func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
+       return b.Clock.Now().Sub(b.startTime)
+}
+
+// Increments the current interval by multiplying it with the multiplier.
+func (b *ExponentialBackOff) incrementCurrentInterval() {
+       // Check for overflow, if overflow is detected set the current interval to the max interval.
+       if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
+               b.currentInterval = b.MaxInterval
+       } else {
+               b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
+       }
+}
+
+// Returns a random value from the following interval:
+//     [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
+func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
+       var delta = randomizationFactor * float64(currentInterval)
+       var minInterval = float64(currentInterval) - delta
+       var maxInterval = float64(currentInterval) + delta
+
+       // Get a random value from the range [minInterval, maxInterval].
+       // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
+       // we want a 33% chance for selecting either 1, 2 or 3.
+       return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
+}
diff --git a/vendor/github.com/cenkalti/backoff/retry.go b/vendor/github.com/cenkalti/backoff/retry.go
new file mode 100644 (file)
index 0000000..e936a50
--- /dev/null
@@ -0,0 +1,82 @@
+package backoff
+
+import "time"
+
+// An Operation is executing by Retry() or RetryNotify().
+// The operation will be retried using a backoff policy if it returns an error.
+type Operation func() error
+
+// Notify is a notify-on-error function. It receives an operation error and
+// backoff delay if the operation failed (with an error).
+//
+// NOTE that if the backoff policy stated to stop retrying,
+// the notify function isn't called.
+type Notify func(error, time.Duration)
+
+// Retry the operation o until it does not return error or BackOff stops.
+// o is guaranteed to be run at least once.
+//
+// If o returns a *PermanentError, the operation is not retried, and the
+// wrapped error is returned.
+//
+// Retry sleeps the goroutine for the duration returned by BackOff after a
+// failed operation returns.
+func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
+
+// RetryNotify calls notify function with the error and wait duration
+// for each failed attempt before sleep.
+func RetryNotify(operation Operation, b BackOff, notify Notify) error {
+       var err error
+       var next time.Duration
+       var t *time.Timer
+
+       cb := ensureContext(b)
+
+       b.Reset()
+       for {
+               if err = operation(); err == nil {
+                       return nil
+               }
+
+               if permanent, ok := err.(*PermanentError); ok {
+                       return permanent.Err
+               }
+
+               if next = cb.NextBackOff(); next == Stop {
+                       return err
+               }
+
+               if notify != nil {
+                       notify(err, next)
+               }
+
+               if t == nil {
+                       t = time.NewTimer(next)
+                       defer t.Stop()
+               } else {
+                       t.Reset(next)
+               }
+
+               select {
+               case <-cb.Context().Done():
+                       return err
+               case <-t.C:
+               }
+       }
+}
+
+// PermanentError signals that the operation should not be retried.
+type PermanentError struct {
+       Err error
+}
+
+func (e *PermanentError) Error() string {
+       return e.Err.Error()
+}
+
+// Permanent wraps the given err in a *PermanentError.
+func Permanent(err error) *PermanentError {
+       return &PermanentError{
+               Err: err,
+       }
+}
diff --git a/vendor/github.com/cenkalti/backoff/ticker.go b/vendor/github.com/cenkalti/backoff/ticker.go
new file mode 100644 (file)
index 0000000..e41084b
--- /dev/null
@@ -0,0 +1,82 @@
+package backoff
+
+import (
+       "sync"
+       "time"
+)
+
+// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
+//
+// Ticks will continue to arrive when the previous operation is still running,
+// so operations that take a while to fail could run in quick succession.
+type Ticker struct {
+       C        <-chan time.Time
+       c        chan time.Time
+       b        BackOffContext
+       stop     chan struct{}
+       stopOnce sync.Once
+}
+
+// NewTicker returns a new Ticker containing a channel that will send
+// the time at times specified by the BackOff argument. Ticker is
+// guaranteed to tick at least once.  The channel is closed when Stop
+// method is called or BackOff stops. It is not safe to manipulate the
+// provided backoff policy (notably calling NextBackOff or Reset)
+// while the ticker is running.
+func NewTicker(b BackOff) *Ticker {
+       c := make(chan time.Time)
+       t := &Ticker{
+               C:    c,
+               c:    c,
+               b:    ensureContext(b),
+               stop: make(chan struct{}),
+       }
+       t.b.Reset()
+       go t.run()
+       return t
+}
+
+// Stop turns off a ticker. After Stop, no more ticks will be sent.
+func (t *Ticker) Stop() {
+       t.stopOnce.Do(func() { close(t.stop) })
+}
+
+func (t *Ticker) run() {
+       c := t.c
+       defer close(c)
+
+       // Ticker is guaranteed to tick at least once.
+       afterC := t.send(time.Now())
+
+       for {
+               if afterC == nil {
+                       return
+               }
+
+               select {
+               case tick := <-afterC:
+                       afterC = t.send(tick)
+               case <-t.stop:
+                       t.c = nil // Prevent future ticks from being sent to the channel.
+                       return
+               case <-t.b.Context().Done():
+                       return
+               }
+       }
+}
+
+func (t *Ticker) send(tick time.Time) <-chan time.Time {
+       select {
+       case t.c <- tick:
+       case <-t.stop:
+               return nil
+       }
+
+       next := t.b.NextBackOff()
+       if next == Stop {
+               t.Stop()
+               return nil
+       }
+
+       return time.After(next)
+}
diff --git a/vendor/github.com/cenkalti/backoff/tries.go b/vendor/github.com/cenkalti/backoff/tries.go
new file mode 100644 (file)
index 0000000..cfeefd9
--- /dev/null
@@ -0,0 +1,35 @@
+package backoff
+
+import "time"
+
+/*
+WithMaxRetries creates a wrapper around another BackOff, which will
+return Stop if NextBackOff() has been called too many times since
+the last time Reset() was called
+
+Note: Implementation is not thread-safe.
+*/
+func WithMaxRetries(b BackOff, max uint64) BackOff {
+       return &backOffTries{delegate: b, maxTries: max}
+}
+
+type backOffTries struct {
+       delegate BackOff
+       maxTries uint64
+       numTries uint64
+}
+
+func (b *backOffTries) NextBackOff() time.Duration {
+       if b.maxTries > 0 {
+               if b.maxTries <= b.numTries {
+                       return Stop
+               }
+               b.numTries++
+       }
+       return b.delegate.NextBackOff()
+}
+
+func (b *backOffTries) Reset() {
+       b.numTries = 0
+       b.delegate.Reset()
+}
diff --git a/vendor/github.com/grandcat/zeroconf/LICENSE b/vendor/github.com/grandcat/zeroconf/LICENSE
new file mode 100644 (file)
index 0000000..9cd4e23
--- /dev/null
@@ -0,0 +1,27 @@
+The MIT License (MIT)
+Copyright (c) 2016 Stefan Smarzly
+Copyright (c) 2014 Oleksandr Lobunets
+
+Note: Copyright for portions of project zeroconf.sd are held by Oleksandr 
+      Lobunets, 2014, as part of project bonjour. All other copyright for 
+      project zeroconf.sd are held by Stefan Smarzly, 2016.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/grandcat/zeroconf/README.md b/vendor/github.com/grandcat/zeroconf/README.md
new file mode 100644 (file)
index 0000000..a4590b8
--- /dev/null
@@ -0,0 +1,108 @@
+ZeroConf: Service Discovery with mDNS
+=====================================
+ZeroConf is a pure Golang library that employs Multicast DNS-SD for
+
+* browsing and resolving services in your network
+* registering own services
+
+in the local network.
+
+It basically implements aspects of the standards
+[RFC 6762](https://tools.ietf.org/html/rfc6762) (mDNS) and
+[RFC 6763](https://tools.ietf.org/html/rfc6763) (DNS-SD).
+Though it does not support all requirements yet, the aim is to provide a complient solution in the long-term with the community.
+
+By now, it should be compatible to [Avahi](http://avahi.org/) (tested) and Apple's Bonjour (untested).
+Target environments: private LAN/Wifi, small or isolated networks.
+
+[![GoDoc](https://godoc.org/github.com/grandcat/zeroconf?status.svg)](https://godoc.org/github.com/grandcat/zeroconf)
+[![Go Report Card](https://goreportcard.com/badge/github.com/grandcat/zeroconf)](https://goreportcard.com/report/github.com/grandcat/zeroconf)
+
+## Install
+Nothing is as easy as that:
+```bash
+$ go get -u github.com/grandcat/zeroconf
+```
+This package requires **Go 1.7** (context in std lib) or later.
+
+## Browse for services in your local network
+
+```go
+// Discover all services on the network (e.g. _workstation._tcp)
+resolver, err := zeroconf.NewResolver(nil)
+if err != nil {
+    log.Fatalln("Failed to initialize resolver:", err.Error())
+}
+
+entries := make(chan *zeroconf.ServiceEntry)
+go func(results <-chan *zeroconf.ServiceEntry) {
+    for entry := range results {
+        log.Println(entry)
+    }
+    log.Println("No more entries.")
+}(entries)
+
+ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
+defer cancel()
+err = resolver.Browse(ctx, "_workstation._tcp", "local.", entries)
+if err != nil {
+    log.Fatalln("Failed to browse:", err.Error())
+}
+
+<-ctx.Done()
+```
+See https://github.com/grandcat/zeroconf/blob/master/examples/resolv/client.go.
+
+## Lookup a specific service instance
+
+```go
+// Example filled soon.
+```
+
+## Register a service
+
+```go
+server, err := zeroconf.Register("GoZeroconf", "_workstation._tcp", "local.", 42424, []string{"txtv=0", "lo=1", "la=2"}, nil)
+if err != nil {
+    panic(err)
+}
+defer server.Shutdown()
+
+// Clean exit.
+sig := make(chan os.Signal, 1)
+signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
+select {
+case <-sig:
+    // Exit by user
+case <-time.After(time.Second * 120):
+    // Exit by timeout
+}
+
+log.Println("Shutting down.")
+```
+See https://github.com/grandcat/zeroconf/blob/master/examples/register/server.go.
+
+## Features and ToDo's
+This list gives a quick impression about the state of this library.
+See what needs to be done and submit a pull request :)
+
+* [x] Browse / Lookup / Register services
+* [x] Multiple IPv6 / IPv4 addresses support
+* [x] Send multiple probes (exp. back-off) if no service answers (*)
+* [ ] Timestamp entries for TTL checks
+* [ ] Compare new multicasts with already received services
+
+_Notes:_
+
+(*) The denoted functionalities might not be 100% standard conform, but should not be a deal breaker.
+    Some test scenarios demonstrated that the overall robustness and performance increases when applying the suggested improvements.
+
+## Credits
+Great thanks to [hashicorp](https://github.com/hashicorp/mdns) and to [oleksandr](https://github.com/oleksandr/bonjour) and all contributing authors for the code this projects bases upon.
+Large parts of the code are still the same.
+
+However, there are several reasons why I decided to create a fork of the original project:
+The previous project seems to be unmaintained. There are several useful pull requests waiting. I merged most of them in this project.
+Still, the implementation has some bugs and lacks some other features that make it quite unreliable in real LAN environments when running continously.
+Last but not least, the aim for this project is to build a solution that targets standard conformance in the long term with the support of the community.
+Though, resiliency should remain a top goal.
\ No newline at end of file
diff --git a/vendor/github.com/grandcat/zeroconf/client.go b/vendor/github.com/grandcat/zeroconf/client.go
new file mode 100644 (file)
index 0000000..53df90a
--- /dev/null
@@ -0,0 +1,458 @@
+package zeroconf
+
+import (
+       "context"
+       "fmt"
+       "log"
+       "net"
+       "strings"
+
+       "golang.org/x/net/ipv4"
+       "golang.org/x/net/ipv6"
+
+       "time"
+
+       "github.com/cenkalti/backoff"
+       "github.com/miekg/dns"
+)
+
+// IPType specifies the IP traffic the client listens for.
+// This does not guarantee that only mDNS entries of this sepcific
+// type passes. E.g. typical mDNS packets distributed via IPv4, often contain
+// both DNS A and AAAA entries.
+type IPType uint8
+
+// Options for IPType.
+const (
+       IPv4        = 0x01
+       IPv6        = 0x02
+       IPv4AndIPv6 = (IPv4 | IPv6) //< Default option.
+)
+
+type clientOpts struct {
+       listenOn IPType
+       ifaces   []net.Interface
+}
+
+// ClientOption fills the option struct to configure intefaces, etc.
+type ClientOption func(*clientOpts)
+
+// SelectIPTraffic selects the type of IP packets (IPv4, IPv6, or both) this
+// instance listens for.
+// This does not guarantee that only mDNS entries of this sepcific
+// type passes. E.g. typical mDNS packets distributed via IPv4, may contain
+// both DNS A and AAAA entries.
+func SelectIPTraffic(t IPType) ClientOption {
+       return func(o *clientOpts) {
+               o.listenOn = t
+       }
+}
+
+// SelectIfaces selects the interfaces to query for mDNS records
+func SelectIfaces(ifaces []net.Interface) ClientOption {
+       return func(o *clientOpts) {
+               o.ifaces = ifaces
+       }
+}
+
+// Resolver acts as entry point for service lookups and to browse the DNS-SD.
+type Resolver struct {
+       c *client
+}
+
+// NewResolver creates a new resolver and joins the UDP multicast groups to
+// listen for mDNS messages.
+func NewResolver(options ...ClientOption) (*Resolver, error) {
+       // Apply default configuration and load supplied options.
+       var conf = clientOpts{
+               listenOn: IPv4AndIPv6,
+       }
+       for _, o := range options {
+               if o != nil {
+                       o(&conf)
+               }
+       }
+
+       c, err := newClient(conf)
+       if err != nil {
+               return nil, err
+       }
+       return &Resolver{
+               c: c,
+       }, nil
+}
+
+// Browse for all services of a given type in a given domain.
+func (r *Resolver) Browse(ctx context.Context, service, domain string, entries chan<- *ServiceEntry) error {
+       params := defaultParams(service)
+       if domain != "" {
+               params.Domain = domain
+       }
+       params.Entries = entries
+       ctx, cancel := context.WithCancel(ctx)
+       go r.c.mainloop(ctx, params)
+
+       err := r.c.query(params)
+       if err != nil {
+               cancel()
+               return err
+       }
+       // If previous probe was ok, it should be fine now. In case of an error later on,
+       // the entries' queue is closed.
+       go func() {
+               if err := r.c.periodicQuery(ctx, params); err != nil {
+                       cancel()
+               }
+       }()
+
+       return nil
+}
+
+// Lookup a specific service by its name and type in a given domain.
+func (r *Resolver) Lookup(ctx context.Context, instance, service, domain string, entries chan<- *ServiceEntry) error {
+       params := defaultParams(service)
+       params.Instance = instance
+       if domain != "" {
+               params.Domain = domain
+       }
+       params.Entries = entries
+       ctx, cancel := context.WithCancel(ctx)
+       go r.c.mainloop(ctx, params)
+       err := r.c.query(params)
+       if err != nil {
+               // cancel mainloop
+               cancel()
+               return err
+       }
+       // If previous probe was ok, it should be fine now. In case of an error later on,
+       // the entries' queue is closed.
+       go func() {
+               if err := r.c.periodicQuery(ctx, params); err != nil {
+                       cancel()
+               }
+       }()
+
+       return nil
+}
+
+// defaultParams returns a default set of QueryParams.
+func defaultParams(service string) *LookupParams {
+       return NewLookupParams("", service, "local", make(chan *ServiceEntry))
+}
+
+// Client structure encapsulates both IPv4/IPv6 UDP connections.
+type client struct {
+       ipv4conn *ipv4.PacketConn
+       ipv6conn *ipv6.PacketConn
+       ifaces   []net.Interface
+}
+
+// Client structure constructor
+func newClient(opts clientOpts) (*client, error) {
+       ifaces := opts.ifaces
+       if len(ifaces) == 0 {
+               ifaces = listMulticastInterfaces()
+       }
+       // IPv4 interfaces
+       var ipv4conn *ipv4.PacketConn
+       if (opts.listenOn & IPv4) > 0 {
+               var err error
+               ipv4conn, err = joinUdp4Multicast(ifaces)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       // IPv6 interfaces
+       var ipv6conn *ipv6.PacketConn
+       if (opts.listenOn & IPv6) > 0 {
+               var err error
+               ipv6conn, err = joinUdp6Multicast(ifaces)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       return &client{
+               ipv4conn: ipv4conn,
+               ipv6conn: ipv6conn,
+               ifaces:   ifaces,
+       }, nil
+}
+
+// Start listeners and waits for the shutdown signal from exit channel
+func (c *client) mainloop(ctx context.Context, params *LookupParams) {
+       // start listening for responses
+       msgCh := make(chan *dns.Msg, 32)
+       if c.ipv4conn != nil {
+               go c.recv(ctx, c.ipv4conn, msgCh)
+       }
+       if c.ipv6conn != nil {
+               go c.recv(ctx, c.ipv6conn, msgCh)
+       }
+
+       // Iterate through channels from listeners goroutines
+       var entries, sentEntries map[string]*ServiceEntry
+       sentEntries = make(map[string]*ServiceEntry)
+       for {
+               select {
+               case <-ctx.Done():
+                       // Context expired. Notify subscriber that we are done here.
+                       params.done()
+                       c.shutdown()
+                       return
+               case msg := <-msgCh:
+                       entries = make(map[string]*ServiceEntry)
+                       sections := append(msg.Answer, msg.Ns...)
+                       sections = append(sections, msg.Extra...)
+
+                       for _, answer := range sections {
+                               switch rr := answer.(type) {
+                               case *dns.PTR:
+                                       if params.ServiceName() != rr.Hdr.Name {
+                                               continue
+                                       }
+                                       if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Ptr {
+                                               continue
+                                       }
+                                       if _, ok := entries[rr.Ptr]; !ok {
+                                               entries[rr.Ptr] = NewServiceEntry(
+                                                       trimDot(strings.Replace(rr.Ptr, rr.Hdr.Name, "", -1)),
+                                                       params.Service,
+                                                       params.Domain)
+                                       }
+                                       entries[rr.Ptr].TTL = rr.Hdr.Ttl
+                               case *dns.SRV:
+                                       if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
+                                               continue
+                                       } else if !strings.HasSuffix(rr.Hdr.Name, params.ServiceName()) {
+                                               continue
+                                       }
+                                       if _, ok := entries[rr.Hdr.Name]; !ok {
+                                               entries[rr.Hdr.Name] = NewServiceEntry(
+                                                       trimDot(strings.Replace(rr.Hdr.Name, params.ServiceName(), "", 1)),
+                                                       params.Service,
+                                                       params.Domain)
+                                       }
+                                       entries[rr.Hdr.Name].HostName = rr.Target
+                                       entries[rr.Hdr.Name].Port = int(rr.Port)
+                                       entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
+                               case *dns.TXT:
+                                       if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
+                                               continue
+                                       } else if !strings.HasSuffix(rr.Hdr.Name, params.ServiceName()) {
+                                               continue
+                                       }
+                                       if _, ok := entries[rr.Hdr.Name]; !ok {
+                                               entries[rr.Hdr.Name] = NewServiceEntry(
+                                                       trimDot(strings.Replace(rr.Hdr.Name, params.ServiceName(), "", 1)),
+                                                       params.Service,
+                                                       params.Domain)
+                                       }
+                                       entries[rr.Hdr.Name].Text = rr.Txt
+                                       entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
+                               }
+                       }
+                       // Associate IPs in a second round as other fields should be filled by now.
+                       for _, answer := range sections {
+                               switch rr := answer.(type) {
+                               case *dns.A:
+                                       for k, e := range entries {
+                                               if e.HostName == rr.Hdr.Name {
+                                                       entries[k].AddrIPv4 = append(entries[k].AddrIPv4, rr.A)
+                                               }
+                                       }
+                               case *dns.AAAA:
+                                       for k, e := range entries {
+                                               if e.HostName == rr.Hdr.Name {
+                                                       entries[k].AddrIPv6 = append(entries[k].AddrIPv6, rr.AAAA)
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               if len(entries) > 0 {
+                       for k, e := range entries {
+                               if e.TTL == 0 {
+                                       delete(entries, k)
+                                       delete(sentEntries, k)
+                                       continue
+                               }
+                               if _, ok := sentEntries[k]; ok {
+                                       continue
+                               }
+
+                               // If this is an DNS-SD query do not throw PTR away.
+                               // It is expected to have only PTR for enumeration
+                               if params.ServiceRecord.ServiceTypeName() != params.ServiceRecord.ServiceName() {
+                                       // Require at least one resolved IP address for ServiceEntry
+                                       // TODO: wait some more time as chances are high both will arrive.
+                                       if len(e.AddrIPv4) == 0 && len(e.AddrIPv6) == 0 {
+                                               continue
+                                       }
+                               }
+                               // Submit entry to subscriber and cache it.
+                               // This is also a point to possibly stop probing actively for a
+                               // service entry.
+                               params.Entries <- e
+                               sentEntries[k] = e
+                               params.disableProbing()
+                       }
+                       // reset entries
+                       entries = make(map[string]*ServiceEntry)
+               }
+       }
+}
+
+// Shutdown client will close currently open connections and channel implicitly.
+func (c *client) shutdown() {
+       if c.ipv4conn != nil {
+               c.ipv4conn.Close()
+       }
+       if c.ipv6conn != nil {
+               c.ipv6conn.Close()
+       }
+}
+
+// Data receiving routine reads from connection, unpacks packets into dns.Msg
+// structures and sends them to a given msgCh channel
+func (c *client) recv(ctx context.Context, l interface{}, msgCh chan *dns.Msg) {
+       var readFrom func([]byte) (n int, src net.Addr, err error)
+
+       switch pConn := l.(type) {
+       case *ipv6.PacketConn:
+               readFrom = func(b []byte) (n int, src net.Addr, err error) {
+                       n, _, src, err = pConn.ReadFrom(b)
+                       return
+               }
+       case *ipv4.PacketConn:
+               readFrom = func(b []byte) (n int, src net.Addr, err error) {
+                       n, _, src, err = pConn.ReadFrom(b)
+                       return
+               }
+
+       default:
+               return
+       }
+
+       buf := make([]byte, 65536)
+       var fatalErr error
+       for {
+               // Handles the following cases:
+               // - ReadFrom aborts with error due to closed UDP connection -> causes ctx cancel
+               // - ReadFrom aborts otherwise.
+               // TODO: the context check can be removed. Verify!
+               if ctx.Err() != nil || fatalErr != nil {
+                       return
+               }
+
+               n, _, err := readFrom(buf)
+               if err != nil {
+                       fatalErr = err
+                       continue
+               }
+               msg := new(dns.Msg)
+               if err := msg.Unpack(buf[:n]); err != nil {
+                       // log.Printf("[WARN] mdns: Failed to unpack packet: %v", err)
+                       continue
+               }
+               select {
+               case msgCh <- msg:
+                       // Submit decoded DNS message and continue.
+               case <-ctx.Done():
+                       // Abort.
+                       return
+               }
+       }
+}
+
+// periodicQuery sens multiple probes until a valid response is received by
+// the main processing loop or some timeout/cancel fires.
+// TODO: move error reporting to shutdown function as periodicQuery is called from
+// go routine context.
+func (c *client) periodicQuery(ctx context.Context, params *LookupParams) error {
+       if params.stopProbing == nil {
+               return nil
+       }
+
+       bo := backoff.NewExponentialBackOff()
+       bo.InitialInterval = 4 * time.Second
+       bo.MaxInterval = 60 * time.Second
+       bo.Reset()
+
+       for {
+               // Do periodic query.
+               if err := c.query(params); err != nil {
+                       return err
+               }
+               // Backoff and cancel logic.
+               wait := bo.NextBackOff()
+               if wait == backoff.Stop {
+                       log.Println("periodicQuery: abort due to timeout")
+                       return nil
+               }
+               select {
+               case <-time.After(wait):
+                       // Wait for next iteration.
+               case <-params.stopProbing:
+                       // Chan is closed (or happened in the past).
+                       // Done here. Received a matching mDNS entry.
+                       return nil
+               case <-ctx.Done():
+                       return ctx.Err()
+
+               }
+       }
+
+}
+
+// Performs the actual query by service name (browse) or service instance name (lookup),
+// start response listeners goroutines and loops over the entries channel.
+func (c *client) query(params *LookupParams) error {
+       var serviceName, serviceInstanceName string
+       serviceName = fmt.Sprintf("%s.%s.", trimDot(params.Service), trimDot(params.Domain))
+       if params.Instance != "" {
+               serviceInstanceName = fmt.Sprintf("%s.%s", params.Instance, serviceName)
+       }
+
+       // send the query
+       m := new(dns.Msg)
+       if serviceInstanceName != "" {
+               m.Question = []dns.Question{
+                       dns.Question{serviceInstanceName, dns.TypeSRV, dns.ClassINET},
+                       dns.Question{serviceInstanceName, dns.TypeTXT, dns.ClassINET},
+               }
+               m.RecursionDesired = false
+       } else {
+               m.SetQuestion(serviceName, dns.TypePTR)
+               m.RecursionDesired = false
+       }
+       if err := c.sendQuery(m); err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// Pack the dns.Msg and write to available connections (multicast)
+func (c *client) sendQuery(msg *dns.Msg) error {
+       buf, err := msg.Pack()
+       if err != nil {
+               return err
+       }
+       if c.ipv4conn != nil {
+               var wcm ipv4.ControlMessage
+               for ifi := range c.ifaces {
+                       wcm.IfIndex = c.ifaces[ifi].Index
+                       c.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
+               }
+       }
+       if c.ipv6conn != nil {
+               var wcm ipv6.ControlMessage
+               for ifi := range c.ifaces {
+                       wcm.IfIndex = c.ifaces[ifi].Index
+                       c.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
+               }
+       }
+       return nil
+}
diff --git a/vendor/github.com/grandcat/zeroconf/connection.go b/vendor/github.com/grandcat/zeroconf/connection.go
new file mode 100644 (file)
index 0000000..598dbb9
--- /dev/null
@@ -0,0 +1,115 @@
+package zeroconf
+
+import (
+       "fmt"
+       "net"
+
+       "golang.org/x/net/ipv4"
+       "golang.org/x/net/ipv6"
+)
+
+var (
+       // Multicast groups used by mDNS
+       mdnsGroupIPv4 = net.IPv4(224, 0, 0, 251)
+       mdnsGroupIPv6 = net.ParseIP("ff02::fb")
+
+       // mDNS wildcard addresses
+       mdnsWildcardAddrIPv4 = &net.UDPAddr{
+               IP:   net.ParseIP("224.0.0.0"),
+               Port: 5353,
+       }
+       mdnsWildcardAddrIPv6 = &net.UDPAddr{
+               IP: net.ParseIP("ff02::"),
+               // IP:   net.ParseIP("fd00::12d3:26e7:48db:e7d"),
+               Port: 5353,
+       }
+
+       // mDNS endpoint addresses
+       ipv4Addr = &net.UDPAddr{
+               IP:   mdnsGroupIPv4,
+               Port: 5353,
+       }
+       ipv6Addr = &net.UDPAddr{
+               IP:   mdnsGroupIPv6,
+               Port: 5353,
+       }
+)
+
+func joinUdp6Multicast(interfaces []net.Interface) (*ipv6.PacketConn, error) {
+       udpConn, err := net.ListenUDP("udp6", mdnsWildcardAddrIPv6)
+       if err != nil {
+               return nil, err
+       }
+
+       // Join multicast groups to receive announcements
+       pkConn := ipv6.NewPacketConn(udpConn)
+       pkConn.SetControlMessage(ipv6.FlagInterface, true)
+
+       if len(interfaces) == 0 {
+               interfaces = listMulticastInterfaces()
+       }
+       // log.Println("Using multicast interfaces: ", interfaces)
+
+       var failedJoins int
+       for _, iface := range interfaces {
+               if err := pkConn.JoinGroup(&iface, &net.UDPAddr{IP: mdnsGroupIPv6}); err != nil {
+                       // log.Println("Udp6 JoinGroup failed for iface ", iface)
+                       failedJoins++
+               }
+       }
+       if failedJoins == len(interfaces) {
+               pkConn.Close()
+               return nil, fmt.Errorf("udp6: failed to join any of these interfaces: %v", interfaces)
+       }
+
+       return pkConn, nil
+}
+
+func joinUdp4Multicast(interfaces []net.Interface) (*ipv4.PacketConn, error) {
+       udpConn, err := net.ListenUDP("udp4", mdnsWildcardAddrIPv4)
+       if err != nil {
+               // log.Printf("[ERR] bonjour: Failed to bind to udp4 mutlicast: %v", err)
+               return nil, err
+       }
+
+       // Join multicast groups to receive announcements
+       pkConn := ipv4.NewPacketConn(udpConn)
+       pkConn.SetControlMessage(ipv4.FlagInterface, true)
+
+       if len(interfaces) == 0 {
+               interfaces = listMulticastInterfaces()
+       }
+       // log.Println("Using multicast interfaces: ", interfaces)
+
+       var failedJoins int
+       for _, iface := range interfaces {
+               if err := pkConn.JoinGroup(&iface, &net.UDPAddr{IP: mdnsGroupIPv4}); err != nil {
+                       // log.Println("Udp4 JoinGroup failed for iface ", iface)
+                       failedJoins++
+               }
+       }
+       if failedJoins == len(interfaces) {
+               pkConn.Close()
+               return nil, fmt.Errorf("udp4: failed to join any of these interfaces: %v", interfaces)
+       }
+
+       return pkConn, nil
+}
+
+func listMulticastInterfaces() []net.Interface {
+       var interfaces []net.Interface
+       ifaces, err := net.Interfaces()
+       if err != nil {
+               return nil
+       }
+       for _, ifi := range ifaces {
+               if (ifi.Flags & net.FlagUp) == 0 {
+                       continue
+               }
+               if (ifi.Flags & net.FlagMulticast) > 0 {
+                       interfaces = append(interfaces, ifi)
+               }
+       }
+
+       return interfaces
+}
diff --git a/vendor/github.com/grandcat/zeroconf/doc.go b/vendor/github.com/grandcat/zeroconf/doc.go
new file mode 100644 (file)
index 0000000..b3e5d47
--- /dev/null
@@ -0,0 +1,14 @@
+// Package zeroconf is a pure Golang library that employs Multicast DNS-SD for
+// browsing and resolving services in your network and registering own services
+// in the local network.
+//
+// It basically implements aspects of the standards
+// RFC 6762 (mDNS) and
+// RFC 6763 (DNS-SD).
+// Though it does not support all requirements yet, the aim is to provide a
+// complient solution in the long-term with the community.
+//
+// By now, it should be compatible to [Avahi](http://avahi.org/) (tested) and
+// Apple's Bonjour (untested). Should work in the most office, home and private
+// environments.
+package zeroconf
diff --git a/vendor/github.com/grandcat/zeroconf/examples/proxyservice/.gitignore b/vendor/github.com/grandcat/zeroconf/examples/proxyservice/.gitignore
new file mode 100644 (file)
index 0000000..e0aa7cd
--- /dev/null
@@ -0,0 +1 @@
+proxyservice
diff --git a/vendor/github.com/grandcat/zeroconf/examples/proxyservice/server.go b/vendor/github.com/grandcat/zeroconf/examples/proxyservice/server.go
new file mode 100644 (file)
index 0000000..92f4d87
--- /dev/null
@@ -0,0 +1,58 @@
+package main
+
+import (
+       "flag"
+       "log"
+       "os"
+       "os/signal"
+       "syscall"
+
+       "time"
+
+       "github.com/grandcat/zeroconf"
+)
+
+var (
+       name     = flag.String("name", "GoZeroconfGo", "The name for the service.")
+       service  = flag.String("service", "_workstation._tcp", "Set the service type of the new service.")
+       domain   = flag.String("domain", "local.", "Set the network domain. Default should be fine.")
+       host     = flag.String("host", "pc1", "Set host name for service.")
+       ip       = flag.String("ip", "::1", "Set IP a service should be reachable.")
+       port     = flag.Int("port", 42424, "Set the port the service is listening to.")
+       waitTime = flag.Int("wait", 10, "Duration in [s] to publish service for.")
+)
+
+func main() {
+       flag.Parse()
+
+       server, err := zeroconf.RegisterProxy(*name, *service, *domain, *port, *host, []string{*ip}, []string{"txtv=0", "lo=1", "la=2"}, nil)
+       if err != nil {
+               panic(err)
+       }
+       defer server.Shutdown()
+       log.Println("Published proxy service:")
+       log.Println("- Name:", *name)
+       log.Println("- Type:", *service)
+       log.Println("- Domain:", *domain)
+       log.Println("- Port:", *port)
+       log.Println("- Host:", *host)
+       log.Println("- IP:", *ip)
+
+       // Clean exit.
+       sig := make(chan os.Signal, 1)
+       signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
+       // Timeout timer.
+       var tc <-chan time.Time
+       if *waitTime > 0 {
+               tc = time.After(time.Second * time.Duration(*waitTime))
+       }
+
+       select {
+       case <-sig:
+               // Exit by user
+       case <-tc:
+               // Exit by timeout
+       }
+
+       log.Println("Shutting down.")
+}
diff --git a/vendor/github.com/grandcat/zeroconf/examples/register/.gitignore b/vendor/github.com/grandcat/zeroconf/examples/register/.gitignore
new file mode 100644 (file)
index 0000000..5d59440
--- /dev/null
@@ -0,0 +1,2 @@
+register
+
diff --git a/vendor/github.com/grandcat/zeroconf/examples/register/server.go b/vendor/github.com/grandcat/zeroconf/examples/register/server.go
new file mode 100644 (file)
index 0000000..a2d15c5
--- /dev/null
@@ -0,0 +1,54 @@
+package main
+
+import (
+       "flag"
+       "log"
+       "os"
+       "os/signal"
+       "syscall"
+
+       "time"
+
+       "github.com/grandcat/zeroconf"
+)
+
+var (
+       name     = flag.String("name", "GoZeroconfGo", "The name for the service.")
+       service  = flag.String("service", "_workstation._tcp", "Set the service type of the new service.")
+       domain   = flag.String("domain", "local.", "Set the network domain. Default should be fine.")
+       port     = flag.Int("port", 42424, "Set the port the service is listening to.")
+       waitTime = flag.Int("wait", 10, "Duration in [s] to publish service for.")
+)
+
+func main() {
+       flag.Parse()
+
+       server, err := zeroconf.Register(*name, *service, *domain, *port, []string{"txtv=0", "lo=1", "la=2"}, nil)
+       if err != nil {
+               panic(err)
+       }
+       defer server.Shutdown()
+       log.Println("Published service:")
+       log.Println("- Name:", *name)
+       log.Println("- Type:", *service)
+       log.Println("- Domain:", *domain)
+       log.Println("- Port:", *port)
+
+       // Clean exit.
+       sig := make(chan os.Signal, 1)
+       signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
+       // Timeout timer.
+       var tc <-chan time.Time
+       if *waitTime > 0 {
+               tc = time.After(time.Second * time.Duration(*waitTime))
+       }
+
+       select {
+       case <-sig:
+               // Exit by user
+       case <-tc:
+               // Exit by timeout
+       }
+
+       log.Println("Shutting down.")
+}
diff --git a/vendor/github.com/grandcat/zeroconf/examples/resolv/.gitignore b/vendor/github.com/grandcat/zeroconf/examples/resolv/.gitignore
new file mode 100644 (file)
index 0000000..b60b107
--- /dev/null
@@ -0,0 +1,2 @@
+resolv
+
diff --git a/vendor/github.com/grandcat/zeroconf/examples/resolv/README.md b/vendor/github.com/grandcat/zeroconf/examples/resolv/README.md
new file mode 100644 (file)
index 0000000..06ab963
--- /dev/null
@@ -0,0 +1,26 @@
+Browse and Resolve
+==================
+Compile:
+```bash
+go build -v
+```
+
+Browse for available services in your local network:
+```bash
+./resolv
+```
+By default, it shows all working stations in your network running
+a mDNS service like Avahi.
+The output should look similar to this one:
+```
+2016/12/04 00:40:23 &{{stefanserver _workstation._tcp local.   } stefan.local. 50051 [] 120 [192.168.42.42] [fd00::86a6:c8ff:fe62:4242]}
+2016/12/04 00:40:23 stefanserver
+2016/12/04 00:40:28 No more entries.
+```
+The `-wait` parameter enables to wait for a specific time until
+it stops listening for new services.
+
+For a list of all possible options, just have a look at:
+```bash
+./resolv --help
+```
\ No newline at end of file
diff --git a/vendor/github.com/grandcat/zeroconf/examples/resolv/client.go b/vendor/github.com/grandcat/zeroconf/examples/resolv/client.go
new file mode 100644 (file)
index 0000000..83186bc
--- /dev/null
@@ -0,0 +1,45 @@
+package main
+
+import (
+       "context"
+       "flag"
+       "log"
+       "time"
+
+       "github.com/grandcat/zeroconf"
+)
+
+var (
+       service  = flag.String("service", "_workstation._tcp", "Set the service category to look for devices.")
+       domain   = flag.String("domain", "local", "Set the search domain. For local networks, default is fine.")
+       waitTime = flag.Int("wait", 10, "Duration in [s] to run discovery.")
+)
+
+func main() {
+       flag.Parse()
+
+       // Discover all services on the network (e.g. _workstation._tcp)
+       resolver, err := zeroconf.NewResolver(nil)
+       if err != nil {
+               log.Fatalln("Failed to initialize resolver:", err.Error())
+       }
+
+       entries := make(chan *zeroconf.ServiceEntry)
+       go func(results <-chan *zeroconf.ServiceEntry) {
+               for entry := range results {
+                       log.Println(entry)
+               }
+               log.Println("No more entries.")
+       }(entries)
+
+       ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(*waitTime))
+       defer cancel()
+       err = resolver.Browse(ctx, *service, *domain, entries)
+       if err != nil {
+               log.Fatalln("Failed to browse:", err.Error())
+       }
+
+       <-ctx.Done()
+       // Wait some additional time to see debug messages on go routine shutdown.
+       time.Sleep(1 * time.Second)
+}
diff --git a/vendor/github.com/grandcat/zeroconf/server.go b/vendor/github.com/grandcat/zeroconf/server.go
new file mode 100644 (file)
index 0000000..8670151
--- /dev/null
@@ -0,0 +1,743 @@
+package zeroconf
+
+import (
+       "fmt"
+       "log"
+       "math/rand"
+       "net"
+       "os"
+       "strings"
+       "sync"
+       "time"
+
+       "golang.org/x/net/ipv4"
+       "golang.org/x/net/ipv6"
+
+       "errors"
+
+       "github.com/miekg/dns"
+)
+
+const (
+       // Number of Multicast responses sent for a query message (default: 1 < x < 9)
+       multicastRepetitions = 2
+)
+
+// Register a service by given arguments. This call will take the system's hostname
+// and lookup IP by that hostname.
+func Register(instance, service, domain string, port int, text []string, ifaces []net.Interface) (*Server, error) {
+       entry := NewServiceEntry(instance, service, domain)
+       entry.Port = port
+       entry.Text = text
+
+       if entry.Instance == "" {
+               return nil, fmt.Errorf("Missing service instance name")
+       }
+       if entry.Service == "" {
+               return nil, fmt.Errorf("Missing service name")
+       }
+       if entry.Domain == "" {
+               entry.Domain = "local."
+       }
+       if entry.Port == 0 {
+               return nil, fmt.Errorf("Missing port")
+       }
+
+       var err error
+       if entry.HostName == "" {
+               entry.HostName, err = os.Hostname()
+               if err != nil {
+                       return nil, fmt.Errorf("Could not determine host")
+               }
+       }
+
+       if !strings.HasSuffix(trimDot(entry.HostName), entry.Domain) {
+               entry.HostName = fmt.Sprintf("%s.%s.", trimDot(entry.HostName), trimDot(entry.Domain))
+       }
+
+       if len(ifaces) == 0 {
+               ifaces = listMulticastInterfaces()
+       }
+
+       for _, iface := range ifaces {
+               v4, v6 := addrsForInterface(&iface)
+               entry.AddrIPv4 = append(entry.AddrIPv4, v4...)
+               entry.AddrIPv6 = append(entry.AddrIPv6, v6...)
+       }
+
+       if entry.AddrIPv4 == nil && entry.AddrIPv6 == nil {
+               return nil, fmt.Errorf("Could not determine host IP addresses")
+       }
+
+       s, err := newServer(ifaces)
+       if err != nil {
+               return nil, err
+       }
+
+       s.service = entry
+       go s.mainloop()
+       go s.probe()
+
+       return s, nil
+}
+
+// RegisterProxy registers a service proxy. This call will skip the hostname/IP lookup and
+// will use the provided values.
+func RegisterProxy(instance, service, domain string, port int, host string, ips []string, text []string, ifaces []net.Interface) (*Server, error) {
+       entry := NewServiceEntry(instance, service, domain)
+       entry.Port = port
+       entry.Text = text
+       entry.HostName = host
+
+       if entry.Instance == "" {
+               return nil, fmt.Errorf("Missing service instance name")
+       }
+       if entry.Service == "" {
+               return nil, fmt.Errorf("Missing service name")
+       }
+       if entry.HostName == "" {
+               return nil, fmt.Errorf("Missing host name")
+       }
+       if entry.Domain == "" {
+               entry.Domain = "local"
+       }
+       if entry.Port == 0 {
+               return nil, fmt.Errorf("Missing port")
+       }
+
+       if !strings.HasSuffix(trimDot(entry.HostName), entry.Domain) {
+               entry.HostName = fmt.Sprintf("%s.%s.", trimDot(entry.HostName), trimDot(entry.Domain))
+       }
+
+       for _, ip := range ips {
+               ipAddr := net.ParseIP(ip)
+               if ipAddr == nil {
+                       return nil, fmt.Errorf("Failed to parse given IP: %v", ip)
+               } else if ipv4 := ipAddr.To4(); ipv4 != nil {
+                       entry.AddrIPv4 = append(entry.AddrIPv4, ipAddr)
+               } else if ipv6 := ipAddr.To16(); ipv6 != nil {
+                       entry.AddrIPv6 = append(entry.AddrIPv6, ipAddr)
+               } else {
+                       return nil, fmt.Errorf("The IP is neither IPv4 nor IPv6: %#v", ipAddr)
+               }
+       }
+
+       if len(ifaces) == 0 {
+               ifaces = listMulticastInterfaces()
+       }
+
+       s, err := newServer(ifaces)
+       if err != nil {
+               return nil, err
+       }
+
+       s.service = entry
+       go s.mainloop()
+       go s.probe()
+
+       return s, nil
+}
+
+const (
+       qClassCacheFlush uint16 = 1 << 15
+)
+
+// Server structure encapsulates both IPv4/IPv6 UDP connections
+type Server struct {
+       service  *ServiceEntry
+       ipv4conn *ipv4.PacketConn
+       ipv6conn *ipv6.PacketConn
+       ifaces   []net.Interface
+
+       shouldShutdown chan struct{}
+       shutdownLock   sync.Mutex
+       shutdownEnd    sync.WaitGroup
+       isShutdown     bool
+       ttl            uint32
+}
+
+// Constructs server structure
+func newServer(ifaces []net.Interface) (*Server, error) {
+       ipv4conn, err4 := joinUdp4Multicast(ifaces)
+       if err4 != nil {
+               log.Printf("[zeroconf] no suitable IPv4 interface: %s", err4.Error())
+       }
+       ipv6conn, err6 := joinUdp6Multicast(ifaces)
+       if err6 != nil {
+               log.Printf("[zeroconf] no suitable IPv6 interface: %s", err6.Error())
+       }
+       if err4 != nil && err6 != nil {
+               // No supported interface left.
+               return nil, fmt.Errorf("No supported interface")
+       }
+
+       s := &Server{
+               ipv4conn:       ipv4conn,
+               ipv6conn:       ipv6conn,
+               ifaces:         ifaces,
+               ttl:            3200,
+               shouldShutdown: make(chan struct{}),
+       }
+
+       return s, nil
+}
+
+// Start listeners and waits for the shutdown signal from exit channel
+func (s *Server) mainloop() {
+       if s.ipv4conn != nil {
+               go s.recv4(s.ipv4conn)
+       }
+       if s.ipv6conn != nil {
+               go s.recv6(s.ipv6conn)
+       }
+}
+
+// Shutdown closes all udp connections and unregisters the service
+func (s *Server) Shutdown() {
+       s.shutdown()
+}
+
+// SetText updates and announces the TXT records
+func (s *Server) SetText(text []string) {
+       s.service.Text = text
+       s.announceText()
+}
+
+// TTL sets the TTL for DNS replies
+func (s *Server) TTL(ttl uint32) {
+       s.ttl = ttl
+}
+
+// Shutdown server will close currently open connections & channel
+func (s *Server) shutdown() error {
+       s.shutdownLock.Lock()
+       defer s.shutdownLock.Unlock()
+       if s.isShutdown {
+               return errors.New("Server is already shutdown")
+       }
+
+       err := s.unregister()
+       if err != nil {
+               return err
+       }
+
+       close(s.shouldShutdown)
+
+       if s.ipv4conn != nil {
+               s.ipv4conn.Close()
+       }
+       if s.ipv6conn != nil {
+               s.ipv6conn.Close()
+       }
+
+       // Wait for connection and routines to be closed
+       s.shutdownEnd.Wait()
+       s.isShutdown = true
+
+       return nil
+}
+
+// recv is a long running routine to receive packets from an interface
+func (s *Server) recv4(c *ipv4.PacketConn) {
+       if c == nil {
+               return
+       }
+       buf := make([]byte, 65536)
+       s.shutdownEnd.Add(1)
+       defer s.shutdownEnd.Done()
+       for {
+               select {
+               case <-s.shouldShutdown:
+                       return
+               default:
+                       var ifIndex int
+                       n, cm, from, err := c.ReadFrom(buf)
+                       if err != nil {
+                               continue
+                       }
+                       if cm != nil {
+                               ifIndex = cm.IfIndex
+                       }
+                       if err := s.parsePacket(buf[:n], ifIndex, from); err != nil {
+                               // log.Printf("[ERR] zeroconf: failed to handle query v4: %v", err)
+                       }
+               }
+       }
+}
+
+// recv is a long running routine to receive packets from an interface
+func (s *Server) recv6(c *ipv6.PacketConn) {
+       if c == nil {
+               return
+       }
+       buf := make([]byte, 65536)
+       s.shutdownEnd.Add(1)
+       defer s.shutdownEnd.Done()
+       for {
+               select {
+               case <-s.shouldShutdown:
+                       return
+               default:
+                       var ifIndex int
+                       n, cm, from, err := c.ReadFrom(buf)
+                       if err != nil {
+                               continue
+                       }
+                       if cm != nil {
+                               ifIndex = cm.IfIndex
+                       }
+                       if err := s.parsePacket(buf[:n], ifIndex, from); err != nil {
+                               // log.Printf("[ERR] zeroconf: failed to handle query v6: %v", err)
+                       }
+               }
+       }
+}
+
+// parsePacket is used to parse an incoming packet
+func (s *Server) parsePacket(packet []byte, ifIndex int, from net.Addr) error {
+       var msg dns.Msg
+       if err := msg.Unpack(packet); err != nil {
+               // log.Printf("[ERR] zeroconf: Failed to unpack packet: %v", err)
+               return err
+       }
+       return s.handleQuery(&msg, ifIndex, from)
+}
+
+// handleQuery is used to handle an incoming query
+func (s *Server) handleQuery(query *dns.Msg, ifIndex int, from net.Addr) error {
+       // Ignore questions with authoritative section for now
+       if len(query.Ns) > 0 {
+               return nil
+       }
+
+       // Handle each question
+       var err error
+       for _, q := range query.Question {
+               resp := dns.Msg{}
+               resp.SetReply(query)
+               resp.Compress = true
+               resp.RecursionDesired = false
+               resp.Authoritative = true
+               resp.Question = nil // RFC6762 section 6 "responses MUST NOT contain any questions"
+               resp.Answer = []dns.RR{}
+               resp.Extra = []dns.RR{}
+               if err = s.handleQuestion(q, &resp, query, ifIndex); err != nil {
+                       // log.Printf("[ERR] zeroconf: failed to handle question %v: %v", q, err)
+                       continue
+               }
+               // Check if there is an answer
+               if len(resp.Answer) == 0 {
+                       continue
+               }
+
+               if isUnicastQuestion(q) {
+                       // Send unicast
+                       if e := s.unicastResponse(&resp, ifIndex, from); e != nil {
+                               err = e
+                       }
+               } else {
+                       // Send mulicast
+                       if e := s.multicastResponse(&resp, ifIndex); e != nil {
+                               err = e
+                       }
+               }
+       }
+
+       return err
+}
+
+// RFC6762 7.1. Known-Answer Suppression
+func isKnownAnswer(resp *dns.Msg, query *dns.Msg) bool {
+       if len(resp.Answer) == 0 || len(query.Answer) == 0 {
+               return false
+       }
+
+       if resp.Answer[0].Header().Rrtype != dns.TypePTR {
+               return false
+       }
+       answer := resp.Answer[0].(*dns.PTR)
+
+       for _, known := range query.Answer {
+               hdr := known.Header()
+               if hdr.Rrtype != answer.Hdr.Rrtype {
+                       continue
+               }
+               ptr := known.(*dns.PTR)
+               if ptr.Ptr == answer.Ptr && hdr.Ttl >= answer.Hdr.Ttl/2 {
+                       // log.Printf("skipping known answer: %v", ptr)
+                       return true
+               }
+       }
+
+       return false
+}
+
+// handleQuestion is used to handle an incoming question
+func (s *Server) handleQuestion(q dns.Question, resp *dns.Msg, query *dns.Msg, ifIndex int) error {
+       if s.service == nil {
+               return nil
+       }
+
+       switch q.Name {
+       case s.service.ServiceTypeName():
+               s.serviceTypeName(resp, s.ttl)
+               if isKnownAnswer(resp, query) {
+                       resp.Answer = nil
+               }
+
+       case s.service.ServiceName():
+               s.composeBrowsingAnswers(resp, ifIndex)
+               if isKnownAnswer(resp, query) {
+                       resp.Answer = nil
+               }
+
+       case s.service.ServiceInstanceName():
+               s.composeLookupAnswers(resp, s.ttl, ifIndex, false)
+       }
+
+       return nil
+}
+
+func (s *Server) composeBrowsingAnswers(resp *dns.Msg, ifIndex int) {
+       ptr := &dns.PTR{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceName(),
+                       Rrtype: dns.TypePTR,
+                       Class:  dns.ClassINET,
+                       Ttl:    s.ttl,
+               },
+               Ptr: s.service.ServiceInstanceName(),
+       }
+       resp.Answer = append(resp.Answer, ptr)
+
+       txt := &dns.TXT{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeTXT,
+                       Class:  dns.ClassINET,
+                       Ttl:    s.ttl,
+               },
+               Txt: s.service.Text,
+       }
+       srv := &dns.SRV{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeSRV,
+                       Class:  dns.ClassINET,
+                       Ttl:    s.ttl,
+               },
+               Priority: 0,
+               Weight:   0,
+               Port:     uint16(s.service.Port),
+               Target:   s.service.HostName,
+       }
+       resp.Extra = append(resp.Extra, srv, txt)
+
+       resp.Extra = s.appendAddrs(resp.Extra, s.ttl, ifIndex, false)
+}
+
+func (s *Server) composeLookupAnswers(resp *dns.Msg, ttl uint32, ifIndex int, flushCache bool) {
+       // From RFC6762
+       //    The most significant bit of the rrclass for a record in the Answer
+       //    Section of a response message is the Multicast DNS cache-flush bit
+       //    and is discussed in more detail below in Section 10.2, "Announcements
+       //    to Flush Outdated Cache Entries".
+       ptr := &dns.PTR{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceName(),
+                       Rrtype: dns.TypePTR,
+                       Class:  dns.ClassINET,
+                       Ttl:    ttl,
+               },
+               Ptr: s.service.ServiceInstanceName(),
+       }
+       srv := &dns.SRV{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeSRV,
+                       Class:  dns.ClassINET | qClassCacheFlush,
+                       Ttl:    ttl,
+               },
+               Priority: 0,
+               Weight:   0,
+               Port:     uint16(s.service.Port),
+               Target:   s.service.HostName,
+       }
+       txt := &dns.TXT{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeTXT,
+                       Class:  dns.ClassINET | qClassCacheFlush,
+                       Ttl:    ttl,
+               },
+               Txt: s.service.Text,
+       }
+       dnssd := &dns.PTR{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceTypeName(),
+                       Rrtype: dns.TypePTR,
+                       Class:  dns.ClassINET,
+                       Ttl:    ttl,
+               },
+               Ptr: s.service.ServiceName(),
+       }
+       resp.Answer = append(resp.Answer, srv, txt, ptr, dnssd)
+
+       resp.Answer = s.appendAddrs(resp.Answer, ttl, ifIndex, flushCache)
+}
+
+func (s *Server) serviceTypeName(resp *dns.Msg, ttl uint32) {
+       // From RFC6762
+       // 9.  Service Type Enumeration
+       //
+       //    For this purpose, a special meta-query is defined.  A DNS query for
+       //    PTR records with the name "_services._dns-sd._udp.<Domain>" yields a
+       //    set of PTR records, where the rdata of each PTR record is the two-
+       //    label <Service> name, plus the same domain, e.g.,
+       //    "_http._tcp.<Domain>".
+       dnssd := &dns.PTR{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceTypeName(),
+                       Rrtype: dns.TypePTR,
+                       Class:  dns.ClassINET,
+                       Ttl:    ttl,
+               },
+               Ptr: s.service.ServiceName(),
+       }
+       resp.Answer = append(resp.Answer, dnssd)
+}
+
+// Perform probing & announcement
+//TODO: implement a proper probing & conflict resolution
+func (s *Server) probe() {
+       q := new(dns.Msg)
+       q.SetQuestion(s.service.ServiceInstanceName(), dns.TypePTR)
+       q.RecursionDesired = false
+
+       srv := &dns.SRV{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeSRV,
+                       Class:  dns.ClassINET,
+                       Ttl:    s.ttl,
+               },
+               Priority: 0,
+               Weight:   0,
+               Port:     uint16(s.service.Port),
+               Target:   s.service.HostName,
+       }
+       txt := &dns.TXT{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeTXT,
+                       Class:  dns.ClassINET,
+                       Ttl:    s.ttl,
+               },
+               Txt: s.service.Text,
+       }
+       q.Ns = []dns.RR{srv, txt}
+
+       randomizer := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+       for i := 0; i < multicastRepetitions; i++ {
+               if err := s.multicastResponse(q, 0); err != nil {
+                       log.Println("[ERR] zeroconf: failed to send probe:", err.Error())
+               }
+               time.Sleep(time.Duration(randomizer.Intn(250)) * time.Millisecond)
+       }
+
+       // From RFC6762
+       //    The Multicast DNS responder MUST send at least two unsolicited
+       //    responses, one second apart. To provide increased robustness against
+       //    packet loss, a responder MAY send up to eight unsolicited responses,
+       //    provided that the interval between unsolicited responses increases by
+       //    at least a factor of two with every response sent.
+       timeout := 1 * time.Second
+       for i := 0; i < multicastRepetitions; i++ {
+               for _, intf := range s.ifaces {
+                       resp := new(dns.Msg)
+                       resp.MsgHdr.Response = true
+                       // TODO: make response authoritative if we are the publisher
+                       resp.Compress = true
+                       resp.Answer = []dns.RR{}
+                       resp.Extra = []dns.RR{}
+                       s.composeLookupAnswers(resp, s.ttl, intf.Index, true)
+                       if err := s.multicastResponse(resp, intf.Index); err != nil {
+                               log.Println("[ERR] zeroconf: failed to send announcement:", err.Error())
+                       }
+               }
+               time.Sleep(timeout)
+               timeout *= 2
+       }
+}
+
+// announceText sends a Text announcement with cache flush enabled
+func (s *Server) announceText() {
+       resp := new(dns.Msg)
+       resp.MsgHdr.Response = true
+
+       txt := &dns.TXT{
+               Hdr: dns.RR_Header{
+                       Name:   s.service.ServiceInstanceName(),
+                       Rrtype: dns.TypeTXT,
+                       Class:  dns.ClassINET | qClassCacheFlush,
+                       Ttl:    s.ttl,
+               },
+               Txt: s.service.Text,
+       }
+
+       resp.Answer = []dns.RR{txt}
+       s.multicastResponse(resp, 0)
+}
+
+func (s *Server) unregister() error {
+       resp := new(dns.Msg)
+       resp.MsgHdr.Response = true
+       resp.Answer = []dns.RR{}
+       resp.Extra = []dns.RR{}
+       s.composeLookupAnswers(resp, 0, 0, true)
+       return s.multicastResponse(resp, 0)
+}
+
+func (s *Server) appendAddrs(list []dns.RR, ttl uint32, ifIndex int, flushCache bool) []dns.RR {
+       v4 := s.service.AddrIPv4
+       v6 := s.service.AddrIPv6
+       if len(v4) == 0 && len(v6) == 0 {
+               iface, _ := net.InterfaceByIndex(ifIndex)
+               if iface != nil {
+                       a4, a6 := addrsForInterface(iface)
+                       v4 = append(v4, a4...)
+                       v6 = append(v6, a6...)
+               }
+       }
+       if ttl > 0 {
+               // RFC6762 Section 10 says A/AAAA records SHOULD
+               // use TTL of 120s, to account for network interface
+               // and IP address changes.
+               ttl = 120
+       }
+       var cacheFlushBit uint16
+       if flushCache {
+               cacheFlushBit = qClassCacheFlush
+       }
+       for _, ipv4 := range v4 {
+               a := &dns.A{
+                       Hdr: dns.RR_Header{
+                               Name:   s.service.HostName,
+                               Rrtype: dns.TypeA,
+                               Class:  dns.ClassINET | cacheFlushBit,
+                               Ttl:    ttl,
+                       },
+                       A: ipv4,
+               }
+               list = append(list, a)
+       }
+       for _, ipv6 := range v6 {
+               aaaa := &dns.AAAA{
+                       Hdr: dns.RR_Header{
+                               Name:   s.service.HostName,
+                               Rrtype: dns.TypeAAAA,
+                               Class:  dns.ClassINET | cacheFlushBit,
+                               Ttl:    ttl,
+                       },
+                       AAAA: ipv6,
+               }
+               list = append(list, aaaa)
+       }
+       return list
+}
+
+func addrsForInterface(iface *net.Interface) ([]net.IP, []net.IP) {
+       var v4, v6, v6local []net.IP
+       addrs, _ := iface.Addrs()
+       for _, address := range addrs {
+               if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+                       if ipnet.IP.To4() != nil {
+                               v4 = append(v4, ipnet.IP)
+                       } else {
+                               switch ip := ipnet.IP.To16(); ip != nil {
+                               case ip.IsGlobalUnicast():
+                                       v6 = append(v6, ipnet.IP)
+                               case ip.IsLinkLocalUnicast():
+                                       v6local = append(v6local, ipnet.IP)
+                               }
+                       }
+               }
+       }
+       if len(v6) == 0 {
+               v6 = v6local
+       }
+       return v4, v6
+}
+
+// unicastResponse is used to send a unicast response packet
+func (s *Server) unicastResponse(resp *dns.Msg, ifIndex int, from net.Addr) error {
+       buf, err := resp.Pack()
+       if err != nil {
+               return err
+       }
+       addr := from.(*net.UDPAddr)
+       if addr.IP.To4() != nil {
+               if ifIndex != 0 {
+                       var wcm ipv4.ControlMessage
+                       wcm.IfIndex = ifIndex
+                       _, err = s.ipv4conn.WriteTo(buf, &wcm, addr)
+               } else {
+                       _, err = s.ipv4conn.WriteTo(buf, nil, addr)
+               }
+               return err
+       } else {
+               if ifIndex != 0 {
+                       var wcm ipv6.ControlMessage
+                       wcm.IfIndex = ifIndex
+                       _, err = s.ipv6conn.WriteTo(buf, &wcm, addr)
+               } else {
+                       _, err = s.ipv6conn.WriteTo(buf, nil, addr)
+               }
+               return err
+       }
+}
+
+// multicastResponse us used to send a multicast response packet
+func (s *Server) multicastResponse(msg *dns.Msg, ifIndex int) error {
+       buf, err := msg.Pack()
+       if err != nil {
+               return err
+       }
+       if s.ipv4conn != nil {
+               var wcm ipv4.ControlMessage
+               if ifIndex != 0 {
+                       wcm.IfIndex = ifIndex
+                       s.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
+               } else {
+                       for _, intf := range s.ifaces {
+                               wcm.IfIndex = intf.Index
+                               s.ipv4conn.WriteTo(buf, &wcm, ipv4Addr)
+                       }
+               }
+       }
+
+       if s.ipv6conn != nil {
+               var wcm ipv6.ControlMessage
+               if ifIndex != 0 {
+                       wcm.IfIndex = ifIndex
+                       s.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
+               } else {
+                       for _, intf := range s.ifaces {
+                               wcm.IfIndex = intf.Index
+                               s.ipv6conn.WriteTo(buf, &wcm, ipv6Addr)
+                       }
+               }
+       }
+       return nil
+}
+
+func isUnicastQuestion(q dns.Question) bool {
+       // From RFC6762
+       // 18.12.  Repurposing of Top Bit of qclass in Question Section
+       //
+       //    In the Question Section of a Multicast DNS query, the top bit of the
+       //    qclass field is used to indicate that unicast responses are preferred
+       //    for this particular question.  (See Section 5.4.)
+       return q.Qclass&qClassCacheFlush != 0
+}
diff --git a/vendor/github.com/grandcat/zeroconf/service.go b/vendor/github.com/grandcat/zeroconf/service.go
new file mode 100644 (file)
index 0000000..b6451f1
--- /dev/null
@@ -0,0 +1,109 @@
+package zeroconf
+
+import (
+       "fmt"
+       "net"
+       "sync"
+)
+
+// ServiceRecord contains the basic description of a service, which contains instance name, service type & domain
+type ServiceRecord struct {
+       Instance string `json:"name"`   // Instance name (e.g. "My web page")
+       Service  string `json:"type"`   // Service name (e.g. _http._tcp.)
+       Domain   string `json:"domain"` // If blank, assumes "local"
+
+       // private variable populated on ServiceRecord creation
+       serviceName         string
+       serviceInstanceName string
+       serviceTypeName     string
+}
+
+// ServiceName returns a complete service name (e.g. _foobar._tcp.local.), which is composed
+// of a service name (also referred as service type) and a domain.
+func (s *ServiceRecord) ServiceName() string {
+       return s.serviceName
+}
+
+// ServiceInstanceName returns a complete service instance name (e.g. MyDemo\ Service._foobar._tcp.local.),
+// which is composed from service instance name, service name and a domain.
+func (s *ServiceRecord) ServiceInstanceName() string {
+       return s.serviceInstanceName
+}
+
+// ServiceTypeName returns the complete identifier for a DNS-SD query.
+func (s *ServiceRecord) ServiceTypeName() string {
+       return s.serviceTypeName
+}
+
+// NewServiceRecord constructs a ServiceRecord.
+func NewServiceRecord(instance, service, domain string) *ServiceRecord {
+       s := &ServiceRecord{
+               Instance:    instance,
+               Service:     service,
+               Domain:      domain,
+               serviceName: fmt.Sprintf("%s.%s.", trimDot(service), trimDot(domain)),
+       }
+
+       // Cache service instance name
+       if instance != "" {
+               s.serviceInstanceName = fmt.Sprintf("%s.%s", trimDot(s.Instance), s.ServiceName())
+       }
+
+       // Cache service type name domain
+       typeNameDomain := "local"
+       if len(s.Domain) > 0 {
+               typeNameDomain = trimDot(s.Domain)
+       }
+       s.serviceTypeName = fmt.Sprintf("_services._dns-sd._udp.%s.", typeNameDomain)
+
+       return s
+}
+
+// LookupParams contains configurable properties to create a service discovery request
+type LookupParams struct {
+       ServiceRecord
+       Entries chan<- *ServiceEntry // Entries Channel
+
+       stopProbing chan struct{}
+       once        sync.Once
+}
+
+// NewLookupParams constructs a LookupParams.
+func NewLookupParams(instance, service, domain string, entries chan<- *ServiceEntry) *LookupParams {
+       return &LookupParams{
+               ServiceRecord: *NewServiceRecord(instance, service, domain),
+               Entries:       entries,
+
+               stopProbing: make(chan struct{}),
+       }
+}
+
+// Notify subscriber that no more entries will arrive. Mostly caused
+// by an expired context.
+func (l *LookupParams) done() {
+       close(l.Entries)
+}
+
+func (l *LookupParams) disableProbing() {
+       l.once.Do(func() { close(l.stopProbing) })
+}
+
+// ServiceEntry represents a browse/lookup result for client API.
+// It is also used to configure service registration (server API), which is
+// used to answer multicast queries.
+type ServiceEntry struct {
+       ServiceRecord
+       HostName string   `json:"hostname"` // Host machine DNS name
+       Port     int      `json:"port"`     // Service Port
+       Text     []string `json:"text"`     // Service info served as a TXT record
+       TTL      uint32   `json:"ttl"`      // TTL of the service record
+       AddrIPv4 []net.IP `json:"-"`        // Host machine IPv4 address
+       AddrIPv6 []net.IP `json:"-"`        // Host machine IPv6 address
+}
+
+// NewServiceEntry constructs a ServiceEntry.
+func NewServiceEntry(instance, service, domain string) *ServiceEntry {
+       return &ServiceEntry{
+               ServiceRecord: *NewServiceRecord(instance, service, domain),
+       }
+}
diff --git a/vendor/github.com/grandcat/zeroconf/utils.go b/vendor/github.com/grandcat/zeroconf/utils.go
new file mode 100644 (file)
index 0000000..39dc158
--- /dev/null
@@ -0,0 +1,8 @@
+package zeroconf
+
+import "strings"
+
+// trimDot is used to trim the dots from the start or end of a string
+func trimDot(s string) string {
+       return strings.Trim(s, ".")
+}
diff --git a/vendor/github.com/miekg/dns/AUTHORS b/vendor/github.com/miekg/dns/AUTHORS
new file mode 100644 (file)
index 0000000..1965683
--- /dev/null
@@ -0,0 +1 @@
+Miek Gieben <miek@miek.nl>
diff --git a/vendor/github.com/miekg/dns/CONTRIBUTORS b/vendor/github.com/miekg/dns/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..5903779
--- /dev/null
@@ -0,0 +1,10 @@
+Alex A. Skinner
+Andrew Tunnell-Jones
+Ask Bjørn Hansen
+Dave Cheney
+Dusty Wilson
+Marek Majkowski
+Peter van Dijk
+Omri Bahumi
+Alex Sergeyev
+James Hartig
diff --git a/vendor/github.com/miekg/dns/COPYRIGHT b/vendor/github.com/miekg/dns/COPYRIGHT
new file mode 100644 (file)
index 0000000..35702b1
--- /dev/null
@@ -0,0 +1,9 @@
+Copyright 2009 The Go Authors. All rights reserved. Use of this source code
+is governed by a BSD-style license that can be found in the LICENSE file.
+Extensions of the original work are copyright (c) 2011 Miek Gieben
+
+Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is
+governed by a BSD-style license that can be found in the LICENSE file.
+
+Copyright 2014 CloudFlare. All rights reserved. Use of this source code is
+governed by a BSD-style license that can be found in the LICENSE file.
diff --git a/vendor/github.com/miekg/dns/Gopkg.lock b/vendor/github.com/miekg/dns/Gopkg.lock
new file mode 100644 (file)
index 0000000..6866322
--- /dev/null
@@ -0,0 +1,57 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  branch = "master"
+  digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870"
+  name = "golang.org/x/crypto"
+  packages = [
+    "ed25519",
+    "ed25519/internal/edwards25519",
+  ]
+  pruneopts = ""
+  revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
+
+[[projects]]
+  branch = "master"
+  digest = "1:08e41d63f8dac84d83797368b56cf0b339e42d0224e5e56668963c28aec95685"
+  name = "golang.org/x/net"
+  packages = [
+    "bpf",
+    "context",
+    "internal/iana",
+    "internal/socket",
+    "ipv4",
+    "ipv6",
+  ]
+  pruneopts = ""
+  revision = "4dfa2610cdf3b287375bbba5b8f2a14d3b01d8de"
+
+[[projects]]
+  branch = "master"
+  digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1"
+  name = "golang.org/x/sync"
+  packages = ["errgroup"]
+  pruneopts = ""
+  revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
+
+[[projects]]
+  branch = "master"
+  digest = "1:149a432fabebb8221a80f77731b1cd63597197ded4f14af606ebe3a0959004ec"
+  name = "golang.org/x/sys"
+  packages = ["unix"]
+  pruneopts = ""
+  revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  input-imports = [
+    "golang.org/x/crypto/ed25519",
+    "golang.org/x/net/ipv4",
+    "golang.org/x/net/ipv6",
+    "golang.org/x/sync/errgroup",
+    "golang.org/x/sys/unix",
+  ]
+  solver-name = "gps-cdcl"
+  solver-version = 1
diff --git a/vendor/github.com/miekg/dns/Gopkg.toml b/vendor/github.com/miekg/dns/Gopkg.toml
new file mode 100644 (file)
index 0000000..85e6ff3
--- /dev/null
@@ -0,0 +1,38 @@
+
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+#   name = "github.com/user/project"
+#   version = "1.0.0"
+#
+# [[constraint]]
+#   name = "github.com/user/project2"
+#   branch = "dev"
+#   source = "github.com/myfork/project2"
+#
+# [[override]]
+#  name = "github.com/x/y"
+#  version = "2.4.0"
+
+
+[[constraint]]
+  branch = "master"
+  name = "golang.org/x/crypto"
+
+[[constraint]]
+  branch = "master"
+  name = "golang.org/x/net"
+
+[[constraint]]
+  branch = "master"
+  name = "golang.org/x/sys"
+
+[[constraint]]
+  branch = "master"
+  name = "golang.org/x/sync"
diff --git a/vendor/github.com/miekg/dns/LICENSE b/vendor/github.com/miekg/dns/LICENSE
new file mode 100644 (file)
index 0000000..5763fa7
--- /dev/null
@@ -0,0 +1,32 @@
+Extensions of the original work are copyright (c) 2011 Miek Gieben
+
+As this is fork of the official Go code the same license applies:
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/vendor/github.com/miekg/dns/Makefile.fuzz b/vendor/github.com/miekg/dns/Makefile.fuzz
new file mode 100644 (file)
index 0000000..dc158c4
--- /dev/null
@@ -0,0 +1,33 @@
+# Makefile for fuzzing
+#
+# Use go-fuzz and needs the tools installed.
+# See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/
+#
+# Installing go-fuzz:
+# $ make -f Makefile.fuzz get
+# Installs:
+# * github.com/dvyukov/go-fuzz/go-fuzz
+# * get github.com/dvyukov/go-fuzz/go-fuzz-build
+
+all: build
+
+.PHONY: build
+build:
+       go-fuzz-build -tags fuzz github.com/miekg/dns
+
+.PHONY: build-newrr
+build-newrr:
+       go-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns
+
+.PHONY: fuzz
+fuzz:
+       go-fuzz -bin=dns-fuzz.zip -workdir=fuzz
+
+.PHONY: get
+get:
+       go get github.com/dvyukov/go-fuzz/go-fuzz
+       go get github.com/dvyukov/go-fuzz/go-fuzz-build
+
+.PHONY: clean
+clean:
+       rm *-fuzz.zip
diff --git a/vendor/github.com/miekg/dns/Makefile.release b/vendor/github.com/miekg/dns/Makefile.release
new file mode 100644 (file)
index 0000000..8fb748e
--- /dev/null
@@ -0,0 +1,52 @@
+# Makefile for releasing.
+#
+# The release is controlled from version.go. The version found there is
+# used to tag the git repo, we're not building any artifects so there is nothing
+# to upload to github.
+#
+# * Up the version in version.go
+# * Run: make -f Makefile.release release
+#   * will *commit* your change with 'Release $VERSION'
+#   * push to github
+#
+
+define GO
+//+build ignore
+
+package main
+
+import (
+       "fmt"
+
+       "github.com/miekg/dns"
+)
+
+func main() {
+       fmt.Println(dns.Version.String())
+}
+endef
+
+$(file > version_release.go,$(GO))
+VERSION:=$(shell go run version_release.go)
+TAG="v$(VERSION)"
+
+all:
+       @echo Use the \'release\' target to start a release $(VERSION)
+       rm -f version_release.go
+
+.PHONY: release
+release: commit push
+       @echo Released $(VERSION)
+       rm -f version_release.go
+
+.PHONY: commit
+commit:
+       @echo Committing release $(VERSION)
+       git commit -am"Release $(VERSION)"
+       git tag $(TAG)
+
+.PHONY: push
+push:
+       @echo Pushing release $(VERSION) to master
+       git push --tags
+       git push
diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md
new file mode 100644 (file)
index 0000000..dc2a822
--- /dev/null
@@ -0,0 +1,172 @@
+[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns)
+[![Code Coverage](https://img.shields.io/codecov/c/github/miekg/dns/master.svg)](https://codecov.io/github/miekg/dns?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns)
+[![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns)
+
+# Alternative (more granular) approach to a DNS library
+
+> Less is more.
+
+Complete and usable DNS library. All Resource Records are supported, including the DNSSEC types.
+It follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there
+isn't a convenience function for it. Server side and client side programming is supported, i.e. you
+can build servers and resolvers with it.
+
+We try to keep the "master" branch as sane as possible and at the bleeding edge of standards,
+avoiding breaking changes wherever reasonable. We support the last two versions of Go.
+
+# Goals
+
+* KISS;
+* Fast;
+* Small API. If it's easy to code in Go, don't make a function for it.
+
+# Users
+
+A not-so-up-to-date-list-that-may-be-actually-current:
+
+* https://github.com/coredns/coredns
+* https://cloudflare.com
+* https://github.com/abh/geodns
+* http://www.statdns.com/
+* http://www.dnsinspect.com/
+* https://github.com/chuangbo/jianbing-dictionary-dns
+* http://www.dns-lg.com/
+* https://github.com/fcambus/rrda
+* https://github.com/kenshinx/godns
+* https://github.com/skynetservices/skydns
+* https://github.com/hashicorp/consul
+* https://github.com/DevelopersPL/godnsagent
+* https://github.com/duedil-ltd/discodns
+* https://github.com/StalkR/dns-reverse-proxy
+* https://github.com/tianon/rawdns
+* https://mesosphere.github.io/mesos-dns/
+* https://pulse.turbobytes.com/
+* https://github.com/fcambus/statzone
+* https://github.com/benschw/dns-clb-go
+* https://github.com/corny/dnscheck for <http://public-dns.info/>
+* https://namesmith.io
+* https://github.com/miekg/unbound
+* https://github.com/miekg/exdns
+* https://dnslookup.org
+* https://github.com/looterz/grimd
+* https://github.com/phamhongviet/serf-dns
+* https://github.com/mehrdadrad/mylg
+* https://github.com/bamarni/dockness
+* https://github.com/fffaraz/microdns
+* http://kelda.io
+* https://github.com/ipdcode/hades <https://jd.com>
+* https://github.com/StackExchange/dnscontrol/
+* https://www.dnsperf.com/
+* https://dnssectest.net/
+* https://dns.apebits.com
+* https://github.com/oif/apex
+* https://github.com/jedisct1/dnscrypt-proxy
+* https://github.com/jedisct1/rpdns
+* https://github.com/xor-gate/sshfp
+* https://github.com/rs/dnstrace
+* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss))
+* https://github.com/semihalev/sdns
+* https://render.com
+* https://github.com/peterzen/goresolver
+
+Send pull request if you want to be listed here.
+
+# Features
+
+* UDP/TCP queries, IPv4 and IPv6
+* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported
+* Fast
+* Server side programming (mimicking the net/http package)
+* Client side programming
+* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519
+* EDNS0, NSID, Cookies
+* AXFR/IXFR
+* TSIG, SIG(0)
+* DNS over TLS (DoT): encrypted connection between client and server over TCP
+* DNS name compression
+
+Have fun!
+
+Miek Gieben  -  2010-2012  -  <miek@miek.nl>
+DNS Authors 2012-
+
+# Building
+
+Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should
+work:
+
+    go get github.com/miekg/dns
+    go build github.com/miekg/dns
+
+## Examples
+
+A short "how to use the API" is at the beginning of doc.go (this also will show when you call `godoc
+github.com/miekg/dns`).
+
+Example programs can be found in the `github.com/miekg/exdns` repository.
+
+## Supported RFCs
+
+*all of them*
+
+* 103{4,5} - DNS standard
+* 1348 - NSAP record (removed the record)
+* 1982 - Serial Arithmetic
+* 1876 - LOC record
+* 1995 - IXFR
+* 1996 - DNS notify
+* 2136 - DNS Update (dynamic updates)
+* 2181 - RRset definition - there is no RRset type though, just []RR
+* 2537 - RSAMD5 DNS keys
+* 2065 - DNSSEC (updated in later RFCs)
+* 2671 - EDNS record
+* 2782 - SRV record
+* 2845 - TSIG record
+* 2915 - NAPTR record
+* 2929 - DNS IANA Considerations
+* 3110 - RSASHA1 DNS keys
+* 3225 - DO bit (DNSSEC OK)
+* 340{1,2,3} - NAPTR record
+* 3445 - Limiting the scope of (DNS)KEY
+* 3597 - Unknown RRs
+* 403{3,4,5} - DNSSEC + validation functions
+* 4255 - SSHFP record
+* 4343 - Case insensitivity
+* 4408 - SPF record
+* 4509 - SHA256 Hash in DS
+* 4592 - Wildcards in the DNS
+* 4635 - HMAC SHA TSIG
+* 4701 - DHCID
+* 4892 - id.server
+* 5001 - NSID
+* 5155 - NSEC3 record
+* 5205 - HIP record
+* 5702 - SHA2 in the DNS
+* 5936 - AXFR
+* 5966 - TCP implementation recommendations
+* 6605 - ECDSA
+* 6725 - IANA Registry Update
+* 6742 - ILNP DNS
+* 6840 - Clarifications and Implementation Notes for DNS Security
+* 6844 - CAA record
+* 6891 - EDNS0 update
+* 6895 - DNS IANA considerations
+* 6975 - Algorithm Understanding in DNSSEC
+* 7043 - EUI48/EUI64 records
+* 7314 - DNS (EDNS) EXPIRE Option
+* 7477 - CSYNC RR
+* 7828 - edns-tcp-keepalive EDNS0 Option
+* 7553 - URI record
+* 7858 - DNS over TLS: Initiation and Performance Considerations
+* 7871 - EDNS0 Client Subnet
+* 7873 - Domain Name System (DNS) Cookies
+* 8080 - EdDSA for DNSSEC
+* 8499 - DNS Terminology
+
+## Loosely Based Upon
+
+* ldns - <https://nlnetlabs.nl/projects/ldns/about/>
+* NSD - <https://nlnetlabs.nl/projects/nsd/about/>
+* Net::DNS - <http://www.net-dns.org/>
+* GRONG - <https://github.com/bortzmeyer/grong>
diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go
new file mode 100644 (file)
index 0000000..78c076c
--- /dev/null
@@ -0,0 +1,56 @@
+package dns
+
+// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError.
+// It returns a MsgAcceptAction to indicate what should happen with the message.
+type MsgAcceptFunc func(dh Header) MsgAcceptAction
+
+// DefaultMsgAcceptFunc checks the request and will reject if:
+//
+// * isn't a request (don't respond in that case).
+// * opcode isn't OpcodeQuery or OpcodeNotify
+// * Zero bit isn't zero
+// * has more than 1 question in the question section
+// * has more than 1 RR in the Answer section
+// * has more than 0 RRs in the Authority section
+// * has more than 2 RRs in the Additional section
+var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc
+
+// MsgAcceptAction represents the action to be taken.
+type MsgAcceptAction int
+
+const (
+       MsgAccept MsgAcceptAction = iota // Accept the message
+       MsgReject                        // Reject the message with a RcodeFormatError
+       MsgIgnore                        // Ignore the error and send nothing back.
+)
+
+func defaultMsgAcceptFunc(dh Header) MsgAcceptAction {
+       if isResponse := dh.Bits&_QR != 0; isResponse {
+               return MsgIgnore
+       }
+
+       // Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs.
+       opcode := int(dh.Bits>>11) & 0xF
+       if opcode != OpcodeQuery && opcode != OpcodeNotify {
+               return MsgReject
+       }
+
+       if isZero := dh.Bits&_Z != 0; isZero {
+               return MsgReject
+       }
+       if dh.Qdcount != 1 {
+               return MsgReject
+       }
+       // NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11.
+       if dh.Ancount > 1 {
+               return MsgReject
+       }
+       // IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3.
+       if dh.Nscount > 1 {
+               return MsgReject
+       }
+       if dh.Arcount > 2 {
+               return MsgReject
+       }
+       return MsgAccept
+}
diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go
new file mode 100644 (file)
index 0000000..599f14c
--- /dev/null
@@ -0,0 +1,476 @@
+package dns
+
+// A client implementation.
+
+import (
+       "bytes"
+       "context"
+       "crypto/tls"
+       "encoding/binary"
+       "io"
+       "net"
+       "strings"
+       "time"
+)
+
+const (
+       dnsTimeout     time.Duration = 2 * time.Second
+       tcpIdleTimeout time.Duration = 8 * time.Second
+)
+
+// A Conn represents a connection to a DNS server.
+type Conn struct {
+       net.Conn                         // a net.Conn holding the connection
+       UDPSize        uint16            // minimum receive buffer for UDP messages
+       TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
+       tsigRequestMAC string
+}
+
+// A Client defines parameters for a DNS client.
+type Client struct {
+       Net       string      // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
+       UDPSize   uint16      // minimum receive buffer for UDP messages
+       TLSConfig *tls.Config // TLS connection configuration
+       Dialer    *net.Dialer // a net.Dialer used to set local address, timeouts and more
+       // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,
+       // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and
+       // Client.Dialer) or context.Context.Deadline (see the deprecated ExchangeContext)
+       Timeout        time.Duration
+       DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
+       ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
+       WriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
+       TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
+       SingleInflight bool              // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
+       group          singleflight
+}
+
+// Exchange performs a synchronous UDP query. It sends the message m to the address
+// contained in a and waits for a reply. Exchange does not retry a failed query, nor
+// will it fall back to TCP in case of truncation.
+// See client.Exchange for more information on setting larger buffer sizes.
+func Exchange(m *Msg, a string) (r *Msg, err error) {
+       client := Client{Net: "udp"}
+       r, _, err = client.Exchange(m, a)
+       return r, err
+}
+
+func (c *Client) dialTimeout() time.Duration {
+       if c.Timeout != 0 {
+               return c.Timeout
+       }
+       if c.DialTimeout != 0 {
+               return c.DialTimeout
+       }
+       return dnsTimeout
+}
+
+func (c *Client) readTimeout() time.Duration {
+       if c.ReadTimeout != 0 {
+               return c.ReadTimeout
+       }
+       return dnsTimeout
+}
+
+func (c *Client) writeTimeout() time.Duration {
+       if c.WriteTimeout != 0 {
+               return c.WriteTimeout
+       }
+       return dnsTimeout
+}
+
+// Dial connects to the address on the named network.
+func (c *Client) Dial(address string) (conn *Conn, err error) {
+       // create a new dialer with the appropriate timeout
+       var d net.Dialer
+       if c.Dialer == nil {
+               d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())}
+       } else {
+               d = *c.Dialer
+       }
+
+       network := c.Net
+       if network == "" {
+               network = "udp"
+       }
+
+       useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls")
+
+       conn = new(Conn)
+       if useTLS {
+               network = strings.TrimSuffix(network, "-tls")
+
+               conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
+       } else {
+               conn.Conn, err = d.Dial(network, address)
+       }
+       if err != nil {
+               return nil, err
+       }
+
+       return conn, nil
+}
+
+// Exchange performs a synchronous query. It sends the message m to the address
+// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
+//
+//     c := new(dns.Client)
+//     in, rtt, err := c.Exchange(message, "127.0.0.1:53")
+//
+// Exchange does not retry a failed query, nor will it fall back to TCP in
+// case of truncation.
+// It is up to the caller to create a message that allows for larger responses to be
+// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
+// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit
+// of 512 bytes
+// To specify a local address or a timeout, the caller has to set the `Client.Dialer`
+// attribute appropriately
+func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) {
+       if !c.SingleInflight {
+               return c.exchange(m, address)
+       }
+
+       t := "nop"
+       if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
+               t = t1
+       }
+       cl := "nop"
+       if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
+               cl = cl1
+       }
+       r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
+               return c.exchange(m, address)
+       })
+       if r != nil && shared {
+               r = r.Copy()
+       }
+       return r, rtt, err
+}
+
+func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+       var co *Conn
+
+       co, err = c.Dial(a)
+
+       if err != nil {
+               return nil, 0, err
+       }
+       defer co.Close()
+
+       opt := m.IsEdns0()
+       // If EDNS0 is used use that for size.
+       if opt != nil && opt.UDPSize() >= MinMsgSize {
+               co.UDPSize = opt.UDPSize()
+       }
+       // Otherwise use the client's configured UDP size.
+       if opt == nil && c.UDPSize >= MinMsgSize {
+               co.UDPSize = c.UDPSize
+       }
+
+       co.TsigSecret = c.TsigSecret
+       t := time.Now()
+       // write with the appropriate write timeout
+       co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
+       if err = co.WriteMsg(m); err != nil {
+               return nil, 0, err
+       }
+
+       co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout())))
+       r, err = co.ReadMsg()
+       if err == nil && r.Id != m.Id {
+               err = ErrId
+       }
+       rtt = time.Since(t)
+       return r, rtt, err
+}
+
+// ReadMsg reads a message from the connection co.
+// If the received message contains a TSIG record the transaction signature
+// is verified. This method always tries to return the message, however if an
+// error is returned there are no guarantees that the returned message is a
+// valid representation of the packet read.
+func (co *Conn) ReadMsg() (*Msg, error) {
+       p, err := co.ReadMsgHeader(nil)
+       if err != nil {
+               return nil, err
+       }
+
+       m := new(Msg)
+       if err := m.Unpack(p); err != nil {
+               // If an error 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 an erroneous message
+               return m, err
+       }
+       if t := m.IsTsig(); t != nil {
+               if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
+                       return m, ErrSecret
+               }
+               // Need to work on the original message p, as that was used to calculate the tsig.
+               err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
+       }
+       return m, err
+}
+
+// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
+// Returns message as a byte slice to be parsed with Msg.Unpack later on.
+// Note that error handling on the message body is not possible as only the header is parsed.
+func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
+       var (
+               p   []byte
+               n   int
+               err error
+       )
+
+       switch t := co.Conn.(type) {
+       case *net.TCPConn, *tls.Conn:
+               r := t.(io.Reader)
+
+               // First two bytes specify the length of the entire message.
+               l, err := tcpMsgLen(r)
+               if err != nil {
+                       return nil, err
+               }
+               p = make([]byte, l)
+               n, err = tcpRead(r, p)
+       default:
+               if co.UDPSize > MinMsgSize {
+                       p = make([]byte, co.UDPSize)
+               } else {
+                       p = make([]byte, MinMsgSize)
+               }
+               n, err = co.Read(p)
+       }
+
+       if err != nil {
+               return nil, err
+       } else if n < headerSize {
+               return nil, ErrShortRead
+       }
+
+       p = p[:n]
+       if hdr != nil {
+               dh, _, err := unpackMsgHdr(p, 0)
+               if err != nil {
+                       return nil, err
+               }
+               *hdr = dh
+       }
+       return p, err
+}
+
+// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
+func tcpMsgLen(t io.Reader) (int, error) {
+       p := []byte{0, 0}
+       n, err := t.Read(p)
+       if err != nil {
+               return 0, err
+       }
+
+       // As seen with my local router/switch, returns 1 byte on the above read,
+       // resulting a a ShortRead. Just write it out (instead of loop) and read the
+       // other byte.
+       if n == 1 {
+               n1, err := t.Read(p[1:])
+               if err != nil {
+                       return 0, err
+               }
+               n += n1
+       }
+
+       if n != 2 {
+               return 0, ErrShortRead
+       }
+       l := binary.BigEndian.Uint16(p)
+       if l == 0 {
+               return 0, ErrShortRead
+       }
+       return int(l), nil
+}
+
+// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
+func tcpRead(t io.Reader, p []byte) (int, error) {
+       n, err := t.Read(p)
+       if err != nil {
+               return n, err
+       }
+       for n < len(p) {
+               j, err := t.Read(p[n:])
+               if err != nil {
+                       return n, err
+               }
+               n += j
+       }
+       return n, err
+}
+
+// Read implements the net.Conn read method.
+func (co *Conn) Read(p []byte) (n int, err error) {
+       if co.Conn == nil {
+               return 0, ErrConnEmpty
+       }
+       if len(p) < 2 {
+               return 0, io.ErrShortBuffer
+       }
+       switch t := co.Conn.(type) {
+       case *net.TCPConn, *tls.Conn:
+               r := t.(io.Reader)
+
+               l, err := tcpMsgLen(r)
+               if err != nil {
+                       return 0, err
+               }
+               if l > len(p) {
+                       return l, io.ErrShortBuffer
+               }
+               return tcpRead(r, p[:l])
+       }
+       // UDP connection
+       return co.Conn.Read(p)
+}
+
+// WriteMsg sends a message through the connection co.
+// If the message m contains a TSIG record the transaction
+// signature is calculated.
+func (co *Conn) WriteMsg(m *Msg) (err error) {
+       var out []byte
+       if t := m.IsTsig(); t != nil {
+               mac := ""
+               if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
+                       return ErrSecret
+               }
+               out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
+               // Set for the next read, although only used in zone transfers
+               co.tsigRequestMAC = mac
+       } else {
+               out, err = m.Pack()
+       }
+       if err != nil {
+               return err
+       }
+       _, err = co.Write(out)
+       return err
+}
+
+// Write implements the net.Conn Write method.
+func (co *Conn) Write(p []byte) (n int, err error) {
+       switch t := co.Conn.(type) {
+       case *net.TCPConn, *tls.Conn:
+               w := t.(io.Writer)
+
+               lp := len(p)
+               if lp < 2 {
+                       return 0, io.ErrShortBuffer
+               }
+               if lp > MaxMsgSize {
+                       return 0, &Error{err: "message too large"}
+               }
+               l := make([]byte, 2, lp+2)
+               binary.BigEndian.PutUint16(l, uint16(lp))
+               p = append(l, p...)
+               n, err := io.Copy(w, bytes.NewReader(p))
+               return int(n), err
+       }
+       return co.Conn.Write(p)
+}
+
+// Return the appropriate timeout for a specific request
+func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration {
+       var requestTimeout time.Duration
+       if c.Timeout != 0 {
+               requestTimeout = c.Timeout
+       } else {
+               requestTimeout = timeout
+       }
+       // net.Dialer.Timeout has priority if smaller than the timeouts computed so
+       // far
+       if c.Dialer != nil && c.Dialer.Timeout != 0 {
+               if c.Dialer.Timeout < requestTimeout {
+                       requestTimeout = c.Dialer.Timeout
+               }
+       }
+       return requestTimeout
+}
+
+// Dial connects to the address on the named network.
+func Dial(network, address string) (conn *Conn, err error) {
+       conn = new(Conn)
+       conn.Conn, err = net.Dial(network, address)
+       if err != nil {
+               return nil, err
+       }
+       return conn, nil
+}
+
+// ExchangeContext performs a synchronous UDP query, like Exchange. It
+// additionally obeys deadlines from the passed Context.
+func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
+       client := Client{Net: "udp"}
+       r, _, err = client.ExchangeContext(ctx, m, a)
+       // ignorint rtt to leave the original ExchangeContext API unchanged, but
+       // this function will go away
+       return r, err
+}
+
+// ExchangeConn performs a synchronous query. It sends the message m via the connection
+// c and waits for a reply. The connection c is not closed by ExchangeConn.
+// Deprecated: This function is going away, but can easily be mimicked:
+//
+//     co := &dns.Conn{Conn: c} // c is your net.Conn
+//     co.WriteMsg(m)
+//     in, _  := co.ReadMsg()
+//     co.Close()
+//
+func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
+       println("dns: ExchangeConn: this function is deprecated")
+       co := new(Conn)
+       co.Conn = c
+       if err = co.WriteMsg(m); err != nil {
+               return nil, err
+       }
+       r, err = co.ReadMsg()
+       if err == nil && r.Id != m.Id {
+               err = ErrId
+       }
+       return r, err
+}
+
+// DialTimeout acts like Dial but takes a timeout.
+func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
+       client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}}
+       return client.Dial(address)
+}
+
+// DialWithTLS connects to the address on the named network with TLS.
+func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
+       if !strings.HasSuffix(network, "-tls") {
+               network += "-tls"
+       }
+       client := Client{Net: network, TLSConfig: tlsConfig}
+       return client.Dial(address)
+}
+
+// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
+func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
+       if !strings.HasSuffix(network, "-tls") {
+               network += "-tls"
+       }
+       client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig}
+       return client.Dial(address)
+}
+
+// ExchangeContext acts like Exchange, but honors the deadline on the provided
+// context, if present. If there is both a context deadline and a configured
+// timeout on the client, the earliest of the two takes effect.
+func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+       var timeout time.Duration
+       if deadline, ok := ctx.Deadline(); !ok {
+               timeout = 0
+       } else {
+               timeout = time.Until(deadline)
+       }
+       // not passing the context to the underlying calls, as the API does not support
+       // context. For timeouts you should set up Client.Dialer and call Client.Exchange.
+       // TODO(tmthrgd,miekg): this is a race condition.
+       c.Dialer = &net.Dialer{Timeout: timeout}
+       return c.Exchange(m, a)
+}
diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go
new file mode 100644 (file)
index 0000000..f13cfa3
--- /dev/null
@@ -0,0 +1,139 @@
+package dns
+
+import (
+       "bufio"
+       "io"
+       "os"
+       "strconv"
+       "strings"
+)
+
+// ClientConfig wraps the contents of the /etc/resolv.conf file.
+type ClientConfig struct {
+       Servers  []string // servers to use
+       Search   []string // suffixes to append to local name
+       Port     string   // what port to use
+       Ndots    int      // number of dots in name to trigger absolute lookup
+       Timeout  int      // seconds before giving up on packet
+       Attempts int      // lost packets before giving up on server, not used in the package dns
+}
+
+// ClientConfigFromFile parses a resolv.conf(5) like file and returns
+// a *ClientConfig.
+func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
+       file, err := os.Open(resolvconf)
+       if err != nil {
+               return nil, err
+       }
+       defer file.Close()
+       return ClientConfigFromReader(file)
+}
+
+// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument
+func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
+       c := new(ClientConfig)
+       scanner := bufio.NewScanner(resolvconf)
+       c.Servers = make([]string, 0)
+       c.Search = make([]string, 0)
+       c.Port = "53"
+       c.Ndots = 1
+       c.Timeout = 5
+       c.Attempts = 2
+
+       for scanner.Scan() {
+               if err := scanner.Err(); err != nil {
+                       return nil, err
+               }
+               line := scanner.Text()
+               f := strings.Fields(line)
+               if len(f) < 1 {
+                       continue
+               }
+               switch f[0] {
+               case "nameserver": // add one name server
+                       if len(f) > 1 {
+                               // One more check: make sure server name is
+                               // just an IP address.  Otherwise we need DNS
+                               // to look it up.
+                               name := f[1]
+                               c.Servers = append(c.Servers, name)
+                       }
+
+               case "domain": // set search path to just this domain
+                       if len(f) > 1 {
+                               c.Search = make([]string, 1)
+                               c.Search[0] = f[1]
+                       } else {
+                               c.Search = make([]string, 0)
+                       }
+
+               case "search": // set search path to given servers
+                       c.Search = make([]string, len(f)-1)
+                       for i := 0; i < len(c.Search); i++ {
+                               c.Search[i] = f[i+1]
+                       }
+
+               case "options": // magic options
+                       for i := 1; i < len(f); i++ {
+                               s := f[i]
+                               switch {
+                               case len(s) >= 6 && s[:6] == "ndots:":
+                                       n, _ := strconv.Atoi(s[6:])
+                                       if n < 0 {
+                                               n = 0
+                                       } else if n > 15 {
+                                               n = 15
+                                       }
+                                       c.Ndots = n
+                               case len(s) >= 8 && s[:8] == "timeout:":
+                                       n, _ := strconv.Atoi(s[8:])
+                                       if n < 1 {
+                                               n = 1
+                                       }
+                                       c.Timeout = n
+                               case len(s) >= 9 && s[:9] == "attempts:":
+                                       n, _ := strconv.Atoi(s[9:])
+                                       if n < 1 {
+                                               n = 1
+                                       }
+                                       c.Attempts = n
+                               case s == "rotate":
+                                       /* not imp */
+                               }
+                       }
+               }
+       }
+       return c, nil
+}
+
+// NameList returns all of the names that should be queried based on the
+// config. It is based off of go's net/dns name building, but it does not
+// check the length of the resulting names.
+func (c *ClientConfig) NameList(name string) []string {
+       // if this domain is already fully qualified, no append needed.
+       if IsFqdn(name) {
+               return []string{name}
+       }
+
+       // Check to see if the name has more labels than Ndots. Do this before making
+       // the domain fully qualified.
+       hasNdots := CountLabel(name) > c.Ndots
+       // Make the domain fully qualified.
+       name = Fqdn(name)
+
+       // Make a list of names based off search.
+       names := []string{}
+
+       // If name has enough dots, try that first.
+       if hasNdots {
+               names = append(names, name)
+       }
+       for _, s := range c.Search {
+               names = append(names, Fqdn(name+s))
+       }
+       // If we didn't have enough dots, try after suffixes.
+       if !hasNdots {
+               names = append(names, name)
+       }
+       return names
+}
diff --git a/vendor/github.com/miekg/dns/dane.go b/vendor/github.com/miekg/dns/dane.go
new file mode 100644 (file)
index 0000000..8c4a14e
--- /dev/null
@@ -0,0 +1,43 @@
+package dns
+
+import (
+       "crypto/sha256"
+       "crypto/sha512"
+       "crypto/x509"
+       "encoding/hex"
+       "errors"
+)
+
+// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.
+func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {
+       switch matchingType {
+       case 0:
+               switch selector {
+               case 0:
+                       return hex.EncodeToString(cert.Raw), nil
+               case 1:
+                       return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil
+               }
+       case 1:
+               h := sha256.New()
+               switch selector {
+               case 0:
+                       h.Write(cert.Raw)
+                       return hex.EncodeToString(h.Sum(nil)), nil
+               case 1:
+                       h.Write(cert.RawSubjectPublicKeyInfo)
+                       return hex.EncodeToString(h.Sum(nil)), nil
+               }
+       case 2:
+               h := sha512.New()
+               switch selector {
+               case 0:
+                       h.Write(cert.Raw)
+                       return hex.EncodeToString(h.Sum(nil)), nil
+               case 1:
+                       h.Write(cert.RawSubjectPublicKeyInfo)
+                       return hex.EncodeToString(h.Sum(nil)), nil
+               }
+       }
+       return "", errors.New("dns: bad MatchingType or Selector")
+}
diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go
new file mode 100644 (file)
index 0000000..391d67a
--- /dev/null
@@ -0,0 +1,364 @@
+package dns
+
+import (
+       "errors"
+       "net"
+       "strconv"
+       "strings"
+)
+
+const hexDigit = "0123456789abcdef"
+
+// Everything is assumed in ClassINET.
+
+// SetReply creates a reply message from a request message.
+func (dns *Msg) SetReply(request *Msg) *Msg {
+       dns.Id = request.Id
+       dns.Response = true
+       dns.Opcode = request.Opcode
+       if dns.Opcode == OpcodeQuery {
+               dns.RecursionDesired = request.RecursionDesired // Copy rd bit
+               dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
+       }
+       dns.Rcode = RcodeSuccess
+       if len(request.Question) > 0 {
+               dns.Question = make([]Question, 1)
+               dns.Question[0] = request.Question[0]
+       }
+       return dns
+}
+
+// SetQuestion creates a question message, it sets the Question
+// section, generates an Id and sets the RecursionDesired (RD)
+// bit to true.
+func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
+       dns.Id = Id()
+       dns.RecursionDesired = true
+       dns.Question = make([]Question, 1)
+       dns.Question[0] = Question{z, t, ClassINET}
+       return dns
+}
+
+// SetNotify creates a notify message, it sets the Question
+// section, generates an Id and sets the Authoritative (AA)
+// bit to true.
+func (dns *Msg) SetNotify(z string) *Msg {
+       dns.Opcode = OpcodeNotify
+       dns.Authoritative = true
+       dns.Id = Id()
+       dns.Question = make([]Question, 1)
+       dns.Question[0] = Question{z, TypeSOA, ClassINET}
+       return dns
+}
+
+// SetRcode creates an error message suitable for the request.
+func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
+       dns.SetReply(request)
+       dns.Rcode = rcode
+       return dns
+}
+
+// SetRcodeFormatError creates a message with FormError set.
+func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
+       dns.Rcode = RcodeFormatError
+       dns.Opcode = OpcodeQuery
+       dns.Response = true
+       dns.Authoritative = false
+       dns.Id = request.Id
+       return dns
+}
+
+// SetUpdate makes the message a dynamic update message. It
+// sets the ZONE section to: z, TypeSOA, ClassINET.
+func (dns *Msg) SetUpdate(z string) *Msg {
+       dns.Id = Id()
+       dns.Response = false
+       dns.Opcode = OpcodeUpdate
+       dns.Compress = false // BIND9 cannot handle compression
+       dns.Question = make([]Question, 1)
+       dns.Question[0] = Question{z, TypeSOA, ClassINET}
+       return dns
+}
+
+// SetIxfr creates message for requesting an IXFR.
+func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {
+       dns.Id = Id()
+       dns.Question = make([]Question, 1)
+       dns.Ns = make([]RR, 1)
+       s := new(SOA)
+       s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
+       s.Serial = serial
+       s.Ns = ns
+       s.Mbox = mbox
+       dns.Question[0] = Question{z, TypeIXFR, ClassINET}
+       dns.Ns[0] = s
+       return dns
+}
+
+// SetAxfr creates message for requesting an AXFR.
+func (dns *Msg) SetAxfr(z string) *Msg {
+       dns.Id = Id()
+       dns.Question = make([]Question, 1)
+       dns.Question[0] = Question{z, TypeAXFR, ClassINET}
+       return dns
+}
+
+// SetTsig appends a TSIG RR to the message.
+// This is only a skeleton TSIG RR that is added as the last RR in the
+// additional section. The Tsig is calculated when the message is being send.
+func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
+       t := new(TSIG)
+       t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
+       t.Algorithm = algo
+       t.Fudge = fudge
+       t.TimeSigned = uint64(timesigned)
+       t.OrigId = dns.Id
+       dns.Extra = append(dns.Extra, t)
+       return dns
+}
+
+// SetEdns0 appends a EDNS0 OPT RR to the message.
+// TSIG should always the last RR in a message.
+func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
+       e := new(OPT)
+       e.Hdr.Name = "."
+       e.Hdr.Rrtype = TypeOPT
+       e.SetUDPSize(udpsize)
+       if do {
+               e.SetDo()
+       }
+       dns.Extra = append(dns.Extra, e)
+       return dns
+}
+
+// IsTsig checks if the message has a TSIG record as the last record
+// in the additional section. It returns the TSIG record found or nil.
+func (dns *Msg) IsTsig() *TSIG {
+       if len(dns.Extra) > 0 {
+               if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
+                       return dns.Extra[len(dns.Extra)-1].(*TSIG)
+               }
+       }
+       return nil
+}
+
+// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
+// record in the additional section will do. It returns the OPT record
+// found or nil.
+func (dns *Msg) IsEdns0() *OPT {
+       // EDNS0 is at the end of the additional section, start there.
+       // We might want to change this to *only* look at the last two
+       // records. So we see TSIG and/or OPT - this a slightly bigger
+       // change though.
+       for i := len(dns.Extra) - 1; i >= 0; i-- {
+               if dns.Extra[i].Header().Rrtype == TypeOPT {
+                       return dns.Extra[i].(*OPT)
+               }
+       }
+       return nil
+}
+
+// IsDomainName checks if s is a valid domain name, it returns the number of
+// labels and true, when a domain name is valid.  Note that non fully qualified
+// domain name is considered valid, in this case the last label is counted in
+// the number of labels.  When false is returned the number of labels is not
+// defined.  Also note that this function is extremely liberal; almost any
+// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
+// label fits in 63 characters and that the entire name will fit into the 255
+// octet wire format limit.
+func IsDomainName(s string) (labels int, ok bool) {
+       // XXX: The logic in this function was copied from packDomainName and
+       // should be kept in sync with that function.
+
+       const lenmsg = 256
+
+       if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata.
+               return 0, false
+       }
+
+       s = Fqdn(s)
+
+       // Each dot ends a segment of the name. Except for escaped dots (\.), which
+       // are normal dots.
+
+       var (
+               off    int
+               begin  int
+               wasDot bool
+       )
+       for i := 0; i < len(s); i++ {
+               switch s[i] {
+               case '\\':
+                       if off+1 > lenmsg {
+                               return labels, false
+                       }
+
+                       // check for \DDD
+                       if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) {
+                               i += 3
+                               begin += 3
+                       } else {
+                               i++
+                               begin++
+                       }
+
+                       wasDot = false
+               case '.':
+                       if wasDot {
+                               // two dots back to back is not legal
+                               return labels, false
+                       }
+                       wasDot = true
+
+                       labelLen := i - begin
+                       if labelLen >= 1<<6 { // top two bits of length must be clear
+                               return labels, false
+                       }
+
+                       // off can already (we're in a loop) be bigger than lenmsg
+                       // this happens when a name isn't fully qualified
+                       off += 1 + labelLen
+                       if off > lenmsg {
+                               return labels, false
+                       }
+
+                       labels++
+                       begin = i + 1
+               default:
+                       wasDot = false
+               }
+       }
+
+       return labels, true
+}
+
+// IsSubDomain checks if child is indeed a child of the parent. If child and parent
+// are the same domain true is returned as well.
+func IsSubDomain(parent, child string) bool {
+       // Entire child is contained in parent
+       return CompareDomainName(parent, child) == CountLabel(parent)
+}
+
+// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
+// The checking is performed on the binary payload.
+func IsMsg(buf []byte) error {
+       // Header
+       if len(buf) < headerSize {
+               return errors.New("dns: bad message header")
+       }
+       // Header: Opcode
+       // TODO(miek): more checks here, e.g. check all header bits.
+       return nil
+}
+
+// IsFqdn checks if a domain name is fully qualified.
+func IsFqdn(s string) bool {
+       s2 := strings.TrimSuffix(s, ".")
+       if s == s2 {
+               return false
+       }
+
+       i := strings.LastIndexFunc(s2, func(r rune) bool {
+               return r != '\\'
+       })
+
+       // Test whether we have an even number of escape sequences before
+       // the dot or none.
+       return (len(s2)-i)%2 != 0
+}
+
+// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181.
+// This means the RRs need to have the same type, name, and class. Returns true
+// if the RR set is valid, otherwise false.
+func IsRRset(rrset []RR) bool {
+       if len(rrset) == 0 {
+               return false
+       }
+       if len(rrset) == 1 {
+               return true
+       }
+       rrHeader := rrset[0].Header()
+       rrType := rrHeader.Rrtype
+       rrClass := rrHeader.Class
+       rrName := rrHeader.Name
+
+       for _, rr := range rrset[1:] {
+               curRRHeader := rr.Header()
+               if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName {
+                       // Mismatch between the records, so this is not a valid rrset for
+                       //signing/verifying
+                       return false
+               }
+       }
+
+       return true
+}
+
+// Fqdn return the fully qualified domain name from s.
+// If s is already fully qualified, it behaves as the identity function.
+func Fqdn(s string) string {
+       if IsFqdn(s) {
+               return s
+       }
+       return s + "."
+}
+
+// Copied from the official Go code.
+
+// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
+// address suitable for reverse DNS (PTR) record lookups or an error if it fails
+// to parse the IP address.
+func ReverseAddr(addr string) (arpa string, err error) {
+       ip := net.ParseIP(addr)
+       if ip == nil {
+               return "", &Error{err: "unrecognized address: " + addr}
+       }
+       if v4 := ip.To4(); v4 != nil {
+               buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa."))
+               // Add it, in reverse, to the buffer
+               for i := len(v4) - 1; i >= 0; i-- {
+                       buf = strconv.AppendInt(buf, int64(v4[i]), 10)
+                       buf = append(buf, '.')
+               }
+               // Append "in-addr.arpa." and return (buf already has the final .)
+               buf = append(buf, "in-addr.arpa."...)
+               return string(buf), nil
+       }
+       // Must be IPv6
+       buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa."))
+       // Add it, in reverse, to the buffer
+       for i := len(ip) - 1; i >= 0; i-- {
+               v := ip[i]
+               buf = append(buf, hexDigit[v&0xF])
+               buf = append(buf, '.')
+               buf = append(buf, hexDigit[v>>4])
+               buf = append(buf, '.')
+       }
+       // Append "ip6.arpa." and return (buf already has the final .)
+       buf = append(buf, "ip6.arpa."...)
+       return string(buf), nil
+}
+
+// String returns the string representation for the type t.
+func (t Type) String() string {
+       if t1, ok := TypeToString[uint16(t)]; ok {
+               return t1
+       }
+       return "TYPE" + strconv.Itoa(int(t))
+}
+
+// String returns the string representation for the class c.
+func (c Class) String() string {
+       if s, ok := ClassToString[uint16(c)]; ok {
+               // Only emit mnemonics when they are unambiguous, specically ANY is in both.
+               if _, ok := StringToType[s]; !ok {
+                       return s
+               }
+       }
+       return "CLASS" + strconv.Itoa(int(c))
+}
+
+// String returns the string representation for the name n.
+func (n Name) String() string {
+       return sprintName(string(n))
+}
diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go
new file mode 100644 (file)
index 0000000..f57337b
--- /dev/null
@@ -0,0 +1,134 @@
+package dns
+
+import "strconv"
+
+const (
+       year68     = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
+       defaultTtl = 3600    // Default internal TTL.
+
+       // DefaultMsgSize is the standard default for messages larger than 512 bytes.
+       DefaultMsgSize = 4096
+       // MinMsgSize is the minimal size of a DNS packet.
+       MinMsgSize = 512
+       // MaxMsgSize is the largest possible DNS packet.
+       MaxMsgSize = 65535
+)
+
+// Error represents a DNS error.
+type Error struct{ err string }
+
+func (e *Error) Error() string {
+       if e == nil {
+               return "dns: <nil>"
+       }
+       return "dns: " + e.err
+}
+
+// An RR represents a resource record.
+type RR interface {
+       // Header returns the header of an resource record. The header contains
+       // everything up to the rdata.
+       Header() *RR_Header
+       // String returns the text representation of the resource record.
+       String() string
+
+       // copy returns a copy of the RR
+       copy() RR
+
+       // len returns the length (in octets) of the compressed or uncompressed RR in wire format.
+       //
+       // If compression is nil, the uncompressed size will be returned, otherwise the compressed
+       // size will be returned and domain names will be added to the map for future compression.
+       len(off int, compression map[string]struct{}) int
+
+       // pack packs the records RDATA into wire format. The header will
+       // already have been packed into msg.
+       pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error)
+
+       // unpack unpacks an RR from wire format.
+       //
+       // This will only be called on a new and empty RR type with only the header populated. It
+       // will only be called if the record's RDATA is non-empty.
+       unpack(msg []byte, off int) (off1 int, err error)
+
+       // parse parses an RR from zone file format.
+       //
+       // This will only be called on a new and empty RR type with only the header populated.
+       parse(c *zlexer, origin, file string) *ParseError
+
+       // isDuplicate returns whether the two RRs are duplicates.
+       isDuplicate(r2 RR) bool
+}
+
+// RR_Header is the header all DNS resource records share.
+type RR_Header struct {
+       Name     string `dns:"cdomain-name"`
+       Rrtype   uint16
+       Class    uint16
+       Ttl      uint32
+       Rdlength uint16 // Length of data after header.
+}
+
+// Header returns itself. This is here to make RR_Header implements the RR interface.
+func (h *RR_Header) Header() *RR_Header { return h }
+
+// Just to implement the RR interface.
+func (h *RR_Header) copy() RR { return nil }
+
+func (h *RR_Header) String() string {
+       var s string
+
+       if h.Rrtype == TypeOPT {
+               s = ";"
+               // and maybe other things
+       }
+
+       s += sprintName(h.Name) + "\t"
+       s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
+       s += Class(h.Class).String() + "\t"
+       s += Type(h.Rrtype).String() + "\t"
+       return s
+}
+
+func (h *RR_Header) len(off int, compression map[string]struct{}) int {
+       l := domainNameLen(h.Name, off, compression, true)
+       l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
+       return l
+}
+
+func (h *RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       // RR_Header has no RDATA to pack.
+       return off, nil
+}
+
+func (h *RR_Header) unpack(msg []byte, off int) (int, error) {
+       panic("dns: internal error: unpack should never be called on RR_Header")
+}
+
+func (h *RR_Header) parse(c *zlexer, origin, file string) *ParseError {
+       panic("dns: internal error: parse should never be called on RR_Header")
+}
+
+// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597.
+func (rr *RFC3597) ToRFC3597(r RR) error {
+       buf := make([]byte, Len(r)*2)
+       headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false)
+       if err != nil {
+               return err
+       }
+       buf = buf[:off]
+
+       *rr = RFC3597{Hdr: *r.Header()}
+       rr.Hdr.Rdlength = uint16(off - headerEnd)
+
+       if noRdata(rr.Hdr) {
+               return nil
+       }
+
+       _, err = rr.unpack(buf, headerEnd)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go
new file mode 100644 (file)
index 0000000..3954d41
--- /dev/null
@@ -0,0 +1,794 @@
+package dns
+
+import (
+       "bytes"
+       "crypto"
+       "crypto/dsa"
+       "crypto/ecdsa"
+       "crypto/elliptic"
+       _ "crypto/md5"
+       "crypto/rand"
+       "crypto/rsa"
+       _ "crypto/sha1"
+       _ "crypto/sha256"
+       _ "crypto/sha512"
+       "encoding/asn1"
+       "encoding/binary"
+       "encoding/hex"
+       "math/big"
+       "sort"
+       "strings"
+       "time"
+
+       "golang.org/x/crypto/ed25519"
+)
+
+// DNSSEC encryption algorithm codes.
+const (
+       _ uint8 = iota
+       RSAMD5
+       DH
+       DSA
+       _ // Skip 4, RFC 6725, section 2.1
+       RSASHA1
+       DSANSEC3SHA1
+       RSASHA1NSEC3SHA1
+       RSASHA256
+       _ // Skip 9, RFC 6725, section 2.1
+       RSASHA512
+       _ // Skip 11, RFC 6725, section 2.1
+       ECCGOST
+       ECDSAP256SHA256
+       ECDSAP384SHA384
+       ED25519
+       ED448
+       INDIRECT   uint8 = 252
+       PRIVATEDNS uint8 = 253 // Private (experimental keys)
+       PRIVATEOID uint8 = 254
+)
+
+// AlgorithmToString is a map of algorithm IDs to algorithm names.
+var AlgorithmToString = map[uint8]string{
+       RSAMD5:           "RSAMD5",
+       DH:               "DH",
+       DSA:              "DSA",
+       RSASHA1:          "RSASHA1",
+       DSANSEC3SHA1:     "DSA-NSEC3-SHA1",
+       RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
+       RSASHA256:        "RSASHA256",
+       RSASHA512:        "RSASHA512",
+       ECCGOST:          "ECC-GOST",
+       ECDSAP256SHA256:  "ECDSAP256SHA256",
+       ECDSAP384SHA384:  "ECDSAP384SHA384",
+       ED25519:          "ED25519",
+       ED448:            "ED448",
+       INDIRECT:         "INDIRECT",
+       PRIVATEDNS:       "PRIVATEDNS",
+       PRIVATEOID:       "PRIVATEOID",
+}
+
+// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
+var AlgorithmToHash = map[uint8]crypto.Hash{
+       RSAMD5:           crypto.MD5, // Deprecated in RFC 6725
+       DSA:              crypto.SHA1,
+       RSASHA1:          crypto.SHA1,
+       RSASHA1NSEC3SHA1: crypto.SHA1,
+       RSASHA256:        crypto.SHA256,
+       ECDSAP256SHA256:  crypto.SHA256,
+       ECDSAP384SHA384:  crypto.SHA384,
+       RSASHA512:        crypto.SHA512,
+       ED25519:          crypto.Hash(0),
+}
+
+// DNSSEC hashing algorithm codes.
+const (
+       _      uint8 = iota
+       SHA1         // RFC 4034
+       SHA256       // RFC 4509
+       GOST94       // RFC 5933
+       SHA384       // Experimental
+       SHA512       // Experimental
+)
+
+// HashToString is a map of hash IDs to names.
+var HashToString = map[uint8]string{
+       SHA1:   "SHA1",
+       SHA256: "SHA256",
+       GOST94: "GOST94",
+       SHA384: "SHA384",
+       SHA512: "SHA512",
+}
+
+// DNSKEY flag values.
+const (
+       SEP    = 1
+       REVOKE = 1 << 7
+       ZONE   = 1 << 8
+)
+
+// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.
+type rrsigWireFmt struct {
+       TypeCovered uint16
+       Algorithm   uint8
+       Labels      uint8
+       OrigTtl     uint32
+       Expiration  uint32
+       Inception   uint32
+       KeyTag      uint16
+       SignerName  string `dns:"domain-name"`
+       /* No Signature */
+}
+
+// Used for converting DNSKEY's rdata to wirefmt.
+type dnskeyWireFmt struct {
+       Flags     uint16
+       Protocol  uint8
+       Algorithm uint8
+       PublicKey string `dns:"base64"`
+       /* Nothing is left out */
+}
+
+func divRoundUp(a, b int) int {
+       return (a + b - 1) / b
+}
+
+// KeyTag calculates the keytag (or key-id) of the DNSKEY.
+func (k *DNSKEY) KeyTag() uint16 {
+       if k == nil {
+               return 0
+       }
+       var keytag int
+       switch k.Algorithm {
+       case RSAMD5:
+               // Look at the bottom two bytes of the modules, which the last
+               // item in the pubkey. We could do this faster by looking directly
+               // at the base64 values. But I'm lazy.
+               modulus, _ := fromBase64([]byte(k.PublicKey))
+               if len(modulus) > 1 {
+                       x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
+                       keytag = int(x)
+               }
+       default:
+               keywire := new(dnskeyWireFmt)
+               keywire.Flags = k.Flags
+               keywire.Protocol = k.Protocol
+               keywire.Algorithm = k.Algorithm
+               keywire.PublicKey = k.PublicKey
+               wire := make([]byte, DefaultMsgSize)
+               n, err := packKeyWire(keywire, wire)
+               if err != nil {
+                       return 0
+               }
+               wire = wire[:n]
+               for i, v := range wire {
+                       if i&1 != 0 {
+                               keytag += int(v) // must be larger than uint32
+                       } else {
+                               keytag += int(v) << 8
+                       }
+               }
+               keytag += keytag >> 16 & 0xFFFF
+               keytag &= 0xFFFF
+       }
+       return uint16(keytag)
+}
+
+// ToDS converts a DNSKEY record to a DS record.
+func (k *DNSKEY) ToDS(h uint8) *DS {
+       if k == nil {
+               return nil
+       }
+       ds := new(DS)
+       ds.Hdr.Name = k.Hdr.Name
+       ds.Hdr.Class = k.Hdr.Class
+       ds.Hdr.Rrtype = TypeDS
+       ds.Hdr.Ttl = k.Hdr.Ttl
+       ds.Algorithm = k.Algorithm
+       ds.DigestType = h
+       ds.KeyTag = k.KeyTag()
+
+       keywire := new(dnskeyWireFmt)
+       keywire.Flags = k.Flags
+       keywire.Protocol = k.Protocol
+       keywire.Algorithm = k.Algorithm
+       keywire.PublicKey = k.PublicKey
+       wire := make([]byte, DefaultMsgSize)
+       n, err := packKeyWire(keywire, wire)
+       if err != nil {
+               return nil
+       }
+       wire = wire[:n]
+
+       owner := make([]byte, 255)
+       off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false)
+       if err1 != nil {
+               return nil
+       }
+       owner = owner[:off]
+       // RFC4034:
+       // digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
+       // "|" denotes concatenation
+       // DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
+
+       var hash crypto.Hash
+       switch h {
+       case SHA1:
+               hash = crypto.SHA1
+       case SHA256:
+               hash = crypto.SHA256
+       case SHA384:
+               hash = crypto.SHA384
+       case SHA512:
+               hash = crypto.SHA512
+       default:
+               return nil
+       }
+
+       s := hash.New()
+       s.Write(owner)
+       s.Write(wire)
+       ds.Digest = hex.EncodeToString(s.Sum(nil))
+       return ds
+}
+
+// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
+func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
+       c := &CDNSKEY{DNSKEY: *k}
+       c.Hdr = k.Hdr
+       c.Hdr.Rrtype = TypeCDNSKEY
+       return c
+}
+
+// ToCDS converts a DS record to a CDS record.
+func (d *DS) ToCDS() *CDS {
+       c := &CDS{DS: *d}
+       c.Hdr = d.Hdr
+       c.Hdr.Rrtype = TypeCDS
+       return c
+}
+
+// Sign signs an RRSet. The signature needs to be filled in with the values:
+// Inception, Expiration, KeyTag, SignerName and Algorithm.  The rest is copied
+// from the RRset. Sign returns a non-nill error when the signing went OK.
+// There is no check if RRSet is a proper (RFC 2181) RRSet.  If OrigTTL is non
+// zero, it is used as-is, otherwise the TTL of the RRset is used as the
+// OrigTTL.
+func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
+       if k == nil {
+               return ErrPrivKey
+       }
+       // s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
+       if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
+               return ErrKey
+       }
+
+       h0 := rrset[0].Header()
+       rr.Hdr.Rrtype = TypeRRSIG
+       rr.Hdr.Name = h0.Name
+       rr.Hdr.Class = h0.Class
+       if rr.OrigTtl == 0 { // If set don't override
+               rr.OrigTtl = h0.Ttl
+       }
+       rr.TypeCovered = h0.Rrtype
+       rr.Labels = uint8(CountLabel(h0.Name))
+
+       if strings.HasPrefix(h0.Name, "*") {
+               rr.Labels-- // wildcard, remove from label count
+       }
+
+       sigwire := new(rrsigWireFmt)
+       sigwire.TypeCovered = rr.TypeCovered
+       sigwire.Algorithm = rr.Algorithm
+       sigwire.Labels = rr.Labels
+       sigwire.OrigTtl = rr.OrigTtl
+       sigwire.Expiration = rr.Expiration
+       sigwire.Inception = rr.Inception
+       sigwire.KeyTag = rr.KeyTag
+       // For signing, lowercase this name
+       sigwire.SignerName = strings.ToLower(rr.SignerName)
+
+       // Create the desired binary blob
+       signdata := make([]byte, DefaultMsgSize)
+       n, err := packSigWire(sigwire, signdata)
+       if err != nil {
+               return err
+       }
+       signdata = signdata[:n]
+       wire, err := rawSignatureData(rrset, rr)
+       if err != nil {
+               return err
+       }
+
+       hash, ok := AlgorithmToHash[rr.Algorithm]
+       if !ok {
+               return ErrAlg
+       }
+
+       switch rr.Algorithm {
+       case ED25519:
+               // ed25519 signs the raw message and performs hashing internally.
+               // All other supported signature schemes operate over the pre-hashed
+               // message, and thus ed25519 must be handled separately here.
+               //
+               // The raw message is passed directly into sign and crypto.Hash(0) is
+               // used to signal to the crypto.Signer that the data has not been hashed.
+               signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
+               if err != nil {
+                       return err
+               }
+
+               rr.Signature = toBase64(signature)
+       default:
+               h := hash.New()
+               h.Write(signdata)
+               h.Write(wire)
+
+               signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
+               if err != nil {
+                       return err
+               }
+
+               rr.Signature = toBase64(signature)
+       }
+
+       return nil
+}
+
+func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
+       signature, err := k.Sign(rand.Reader, hashed, hash)
+       if err != nil {
+               return nil, err
+       }
+
+       switch alg {
+       case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
+               return signature, nil
+
+       case ECDSAP256SHA256, ECDSAP384SHA384:
+               ecdsaSignature := &struct {
+                       R, S *big.Int
+               }{}
+               if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {
+                       return nil, err
+               }
+
+               var intlen int
+               switch alg {
+               case ECDSAP256SHA256:
+                       intlen = 32
+               case ECDSAP384SHA384:
+                       intlen = 48
+               }
+
+               signature := intToBytes(ecdsaSignature.R, intlen)
+               signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
+               return signature, nil
+
+       // There is no defined interface for what a DSA backed crypto.Signer returns
+       case DSA, DSANSEC3SHA1:
+               //      t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
+               //      signature := []byte{byte(t)}
+               //      signature = append(signature, intToBytes(r1, 20)...)
+               //      signature = append(signature, intToBytes(s1, 20)...)
+               //      rr.Signature = signature
+
+       case ED25519:
+               return signature, nil
+       }
+
+       return nil, ErrAlg
+}
+
+// Verify validates an RRSet with the signature and key. This is only the
+// cryptographic test, the signature validity period must be checked separately.
+// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
+func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
+       // First the easy checks
+       if !IsRRset(rrset) {
+               return ErrRRset
+       }
+       if rr.KeyTag != k.KeyTag() {
+               return ErrKey
+       }
+       if rr.Hdr.Class != k.Hdr.Class {
+               return ErrKey
+       }
+       if rr.Algorithm != k.Algorithm {
+               return ErrKey
+       }
+       if !strings.EqualFold(rr.SignerName, k.Hdr.Name) {
+               return ErrKey
+       }
+       if k.Protocol != 3 {
+               return ErrKey
+       }
+
+       // IsRRset checked that we have at least one RR and that the RRs in
+       // the set have consistent type, class, and name. Also check that type and
+       // class matches the RRSIG record.
+       if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered {
+               return ErrRRset
+       }
+
+       // RFC 4035 5.3.2.  Reconstructing the Signed Data
+       // Copy the sig, except the rrsig data
+       sigwire := new(rrsigWireFmt)
+       sigwire.TypeCovered = rr.TypeCovered
+       sigwire.Algorithm = rr.Algorithm
+       sigwire.Labels = rr.Labels
+       sigwire.OrigTtl = rr.OrigTtl
+       sigwire.Expiration = rr.Expiration
+       sigwire.Inception = rr.Inception
+       sigwire.KeyTag = rr.KeyTag
+       sigwire.SignerName = strings.ToLower(rr.SignerName)
+       // Create the desired binary blob
+       signeddata := make([]byte, DefaultMsgSize)
+       n, err := packSigWire(sigwire, signeddata)
+       if err != nil {
+               return err
+       }
+       signeddata = signeddata[:n]
+       wire, err := rawSignatureData(rrset, rr)
+       if err != nil {
+               return err
+       }
+
+       sigbuf := rr.sigBuf()           // Get the binary signature data
+       if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
+               // TODO(miek)
+               // remove the domain name and assume its ours?
+       }
+
+       hash, ok := AlgorithmToHash[rr.Algorithm]
+       if !ok {
+               return ErrAlg
+       }
+
+       switch rr.Algorithm {
+       case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5:
+               // TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
+               pubkey := k.publicKeyRSA() // Get the key
+               if pubkey == nil {
+                       return ErrKey
+               }
+
+               h := hash.New()
+               h.Write(signeddata)
+               h.Write(wire)
+               return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
+
+       case ECDSAP256SHA256, ECDSAP384SHA384:
+               pubkey := k.publicKeyECDSA()
+               if pubkey == nil {
+                       return ErrKey
+               }
+
+               // Split sigbuf into the r and s coordinates
+               r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
+               s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
+
+               h := hash.New()
+               h.Write(signeddata)
+               h.Write(wire)
+               if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
+                       return nil
+               }
+               return ErrSig
+
+       case ED25519:
+               pubkey := k.publicKeyED25519()
+               if pubkey == nil {
+                       return ErrKey
+               }
+
+               if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) {
+                       return nil
+               }
+               return ErrSig
+
+       default:
+               return ErrAlg
+       }
+}
+
+// ValidityPeriod uses RFC1982 serial arithmetic to calculate
+// if a signature period is valid. If t is the zero time, the
+// current time is taken other t is. Returns true if the signature
+// is valid at the given time, otherwise returns false.
+func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
+       var utc int64
+       if t.IsZero() {
+               utc = time.Now().UTC().Unix()
+       } else {
+               utc = t.UTC().Unix()
+       }
+       modi := (int64(rr.Inception) - utc) / year68
+       mode := (int64(rr.Expiration) - utc) / year68
+       ti := int64(rr.Inception) + modi*year68
+       te := int64(rr.Expiration) + mode*year68
+       return ti <= utc && utc <= te
+}
+
+// Return the signatures base64 encodedig sigdata as a byte slice.
+func (rr *RRSIG) sigBuf() []byte {
+       sigbuf, err := fromBase64([]byte(rr.Signature))
+       if err != nil {
+               return nil
+       }
+       return sigbuf
+}
+
+// publicKeyRSA returns the RSA public key from a DNSKEY record.
+func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
+       keybuf, err := fromBase64([]byte(k.PublicKey))
+       if err != nil {
+               return nil
+       }
+
+       if len(keybuf) < 1+1+64 {
+               // Exponent must be at least 1 byte and modulus at least 64
+               return nil
+       }
+
+       // RFC 2537/3110, section 2. RSA Public KEY Resource Records
+       // Length is in the 0th byte, unless its zero, then it
+       // it in bytes 1 and 2 and its a 16 bit number
+       explen := uint16(keybuf[0])
+       keyoff := 1
+       if explen == 0 {
+               explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
+               keyoff = 3
+       }
+
+       if explen > 4 || explen == 0 || keybuf[keyoff] == 0 {
+               // Exponent larger than supported by the crypto package,
+               // empty, or contains prohibited leading zero.
+               return nil
+       }
+
+       modoff := keyoff + int(explen)
+       modlen := len(keybuf) - modoff
+       if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 {
+               // Modulus is too small, large, or contains prohibited leading zero.
+               return nil
+       }
+
+       pubkey := new(rsa.PublicKey)
+
+       var expo uint64
+       for i := 0; i < int(explen); i++ {
+               expo <<= 8
+               expo |= uint64(keybuf[keyoff+i])
+       }
+       if expo > 1<<31-1 {
+               // Larger exponent than supported by the crypto package.
+               return nil
+       }
+       pubkey.E = int(expo)
+
+       pubkey.N = big.NewInt(0)
+       pubkey.N.SetBytes(keybuf[modoff:])
+
+       return pubkey
+}
+
+// publicKeyECDSA returns the Curve public key from the DNSKEY record.
+func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
+       keybuf, err := fromBase64([]byte(k.PublicKey))
+       if err != nil {
+               return nil
+       }
+       pubkey := new(ecdsa.PublicKey)
+       switch k.Algorithm {
+       case ECDSAP256SHA256:
+               pubkey.Curve = elliptic.P256()
+               if len(keybuf) != 64 {
+                       // wrongly encoded key
+                       return nil
+               }
+       case ECDSAP384SHA384:
+               pubkey.Curve = elliptic.P384()
+               if len(keybuf) != 96 {
+                       // Wrongly encoded key
+                       return nil
+               }
+       }
+       pubkey.X = big.NewInt(0)
+       pubkey.X.SetBytes(keybuf[:len(keybuf)/2])
+       pubkey.Y = big.NewInt(0)
+       pubkey.Y.SetBytes(keybuf[len(keybuf)/2:])
+       return pubkey
+}
+
+func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
+       keybuf, err := fromBase64([]byte(k.PublicKey))
+       if err != nil {
+               return nil
+       }
+       if len(keybuf) < 22 {
+               return nil
+       }
+       t, keybuf := int(keybuf[0]), keybuf[1:]
+       size := 64 + t*8
+       q, keybuf := keybuf[:20], keybuf[20:]
+       if len(keybuf) != 3*size {
+               return nil
+       }
+       p, keybuf := keybuf[:size], keybuf[size:]
+       g, y := keybuf[:size], keybuf[size:]
+       pubkey := new(dsa.PublicKey)
+       pubkey.Parameters.Q = big.NewInt(0).SetBytes(q)
+       pubkey.Parameters.P = big.NewInt(0).SetBytes(p)
+       pubkey.Parameters.G = big.NewInt(0).SetBytes(g)
+       pubkey.Y = big.NewInt(0).SetBytes(y)
+       return pubkey
+}
+
+func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
+       keybuf, err := fromBase64([]byte(k.PublicKey))
+       if err != nil {
+               return nil
+       }
+       if len(keybuf) != ed25519.PublicKeySize {
+               return nil
+       }
+       return keybuf
+}
+
+type wireSlice [][]byte
+
+func (p wireSlice) Len() int      { return len(p) }
+func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p wireSlice) Less(i, j int) bool {
+       _, ioff, _ := UnpackDomainName(p[i], 0)
+       _, joff, _ := UnpackDomainName(p[j], 0)
+       return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0
+}
+
+// Return the raw signature data.
+func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
+       wires := make(wireSlice, len(rrset))
+       for i, r := range rrset {
+               r1 := r.copy()
+               h := r1.Header()
+               h.Ttl = s.OrigTtl
+               labels := SplitDomainName(h.Name)
+               // 6.2. Canonical RR Form. (4) - wildcards
+               if len(labels) > int(s.Labels) {
+                       // Wildcard
+                       h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
+               }
+               // RFC 4034: 6.2.  Canonical RR Form. (2) - domain name to lowercase
+               h.Name = strings.ToLower(h.Name)
+               // 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
+               //   NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
+               //   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
+               //   SRV, DNAME, A6
+               //
+               // RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
+               //      Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
+               //      that needs conversion to lowercase, and twice at that.  Since HINFO
+               //      records contain no domain names, they are not subject to case
+               //      conversion.
+               switch x := r1.(type) {
+               case *NS:
+                       x.Ns = strings.ToLower(x.Ns)
+               case *MD:
+                       x.Md = strings.ToLower(x.Md)
+               case *MF:
+                       x.Mf = strings.ToLower(x.Mf)
+               case *CNAME:
+                       x.Target = strings.ToLower(x.Target)
+               case *SOA:
+                       x.Ns = strings.ToLower(x.Ns)
+                       x.Mbox = strings.ToLower(x.Mbox)
+               case *MB:
+                       x.Mb = strings.ToLower(x.Mb)
+               case *MG:
+                       x.Mg = strings.ToLower(x.Mg)
+               case *MR:
+                       x.Mr = strings.ToLower(x.Mr)
+               case *PTR:
+                       x.Ptr = strings.ToLower(x.Ptr)
+               case *MINFO:
+                       x.Rmail = strings.ToLower(x.Rmail)
+                       x.Email = strings.ToLower(x.Email)
+               case *MX:
+                       x.Mx = strings.ToLower(x.Mx)
+               case *RP:
+                       x.Mbox = strings.ToLower(x.Mbox)
+                       x.Txt = strings.ToLower(x.Txt)
+               case *AFSDB:
+                       x.Hostname = strings.ToLower(x.Hostname)
+               case *RT:
+                       x.Host = strings.ToLower(x.Host)
+               case *SIG:
+                       x.SignerName = strings.ToLower(x.SignerName)
+               case *PX:
+                       x.Map822 = strings.ToLower(x.Map822)
+                       x.Mapx400 = strings.ToLower(x.Mapx400)
+               case *NAPTR:
+                       x.Replacement = strings.ToLower(x.Replacement)
+               case *KX:
+                       x.Exchanger = strings.ToLower(x.Exchanger)
+               case *SRV:
+                       x.Target = strings.ToLower(x.Target)
+               case *DNAME:
+                       x.Target = strings.ToLower(x.Target)
+               }
+               // 6.2. Canonical RR Form. (5) - origTTL
+               wire := make([]byte, Len(r1)+1) // +1 to be safe(r)
+               off, err1 := PackRR(r1, wire, 0, nil, false)
+               if err1 != nil {
+                       return nil, err1
+               }
+               wire = wire[:off]
+               wires[i] = wire
+       }
+       sort.Sort(wires)
+       for i, wire := range wires {
+               if i > 0 && bytes.Equal(wire, wires[i-1]) {
+                       continue
+               }
+               buf = append(buf, wire...)
+       }
+       return buf, nil
+}
+
+func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {
+       // copied from zmsg.go RRSIG packing
+       off, err := packUint16(sw.TypeCovered, msg, 0)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(sw.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(sw.Labels, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(sw.OrigTtl, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(sw.Expiration, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(sw.Inception, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(sw.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = PackDomainName(sw.SignerName, msg, off, nil, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {
+       // copied from zmsg.go DNSKEY packing
+       off, err := packUint16(dw.Flags, msg, 0)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(dw.Protocol, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(dw.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(dw.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go
new file mode 100644 (file)
index 0000000..33e913a
--- /dev/null
@@ -0,0 +1,178 @@
+package dns
+
+import (
+       "crypto"
+       "crypto/dsa"
+       "crypto/ecdsa"
+       "crypto/elliptic"
+       "crypto/rand"
+       "crypto/rsa"
+       "math/big"
+
+       "golang.org/x/crypto/ed25519"
+)
+
+// Generate generates a DNSKEY of the given bit size.
+// The public part is put inside the DNSKEY record.
+// The Algorithm in the key must be set as this will define
+// what kind of DNSKEY will be generated.
+// The ECDSA algorithms imply a fixed keysize, in that case
+// bits should be set to the size of the algorithm.
+func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
+       switch k.Algorithm {
+       case DSA, DSANSEC3SHA1:
+               if bits != 1024 {
+                       return nil, ErrKeySize
+               }
+       case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1:
+               if bits < 512 || bits > 4096 {
+                       return nil, ErrKeySize
+               }
+       case RSASHA512:
+               if bits < 1024 || bits > 4096 {
+                       return nil, ErrKeySize
+               }
+       case ECDSAP256SHA256:
+               if bits != 256 {
+                       return nil, ErrKeySize
+               }
+       case ECDSAP384SHA384:
+               if bits != 384 {
+                       return nil, ErrKeySize
+               }
+       case ED25519:
+               if bits != 256 {
+                       return nil, ErrKeySize
+               }
+       }
+
+       switch k.Algorithm {
+       case DSA, DSANSEC3SHA1:
+               params := new(dsa.Parameters)
+               if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil {
+                       return nil, err
+               }
+               priv := new(dsa.PrivateKey)
+               priv.PublicKey.Parameters = *params
+               err := dsa.GenerateKey(priv, rand.Reader)
+               if err != nil {
+                       return nil, err
+               }
+               k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
+               return priv, nil
+       case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
+               priv, err := rsa.GenerateKey(rand.Reader, bits)
+               if err != nil {
+                       return nil, err
+               }
+               k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
+               return priv, nil
+       case ECDSAP256SHA256, ECDSAP384SHA384:
+               var c elliptic.Curve
+               switch k.Algorithm {
+               case ECDSAP256SHA256:
+                       c = elliptic.P256()
+               case ECDSAP384SHA384:
+                       c = elliptic.P384()
+               }
+               priv, err := ecdsa.GenerateKey(c, rand.Reader)
+               if err != nil {
+                       return nil, err
+               }
+               k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
+               return priv, nil
+       case ED25519:
+               pub, priv, err := ed25519.GenerateKey(rand.Reader)
+               if err != nil {
+                       return nil, err
+               }
+               k.setPublicKeyED25519(pub)
+               return priv, nil
+       default:
+               return nil, ErrAlg
+       }
+}
+
+// Set the public key (the value E and N)
+func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool {
+       if _E == 0 || _N == nil {
+               return false
+       }
+       buf := exponentToBuf(_E)
+       buf = append(buf, _N.Bytes()...)
+       k.PublicKey = toBase64(buf)
+       return true
+}
+
+// Set the public key for Elliptic Curves
+func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool {
+       if _X == nil || _Y == nil {
+               return false
+       }
+       var intlen int
+       switch k.Algorithm {
+       case ECDSAP256SHA256:
+               intlen = 32
+       case ECDSAP384SHA384:
+               intlen = 48
+       }
+       k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen))
+       return true
+}
+
+// Set the public key for DSA
+func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
+       if _Q == nil || _P == nil || _G == nil || _Y == nil {
+               return false
+       }
+       buf := dsaToBuf(_Q, _P, _G, _Y)
+       k.PublicKey = toBase64(buf)
+       return true
+}
+
+// Set the public key for Ed25519
+func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool {
+       if _K == nil {
+               return false
+       }
+       k.PublicKey = toBase64(_K)
+       return true
+}
+
+// Set the public key (the values E and N) for RSA
+// RFC 3110: Section 2. RSA Public KEY Resource Records
+func exponentToBuf(_E int) []byte {
+       var buf []byte
+       i := big.NewInt(int64(_E)).Bytes()
+       if len(i) < 256 {
+               buf = make([]byte, 1, 1+len(i))
+               buf[0] = uint8(len(i))
+       } else {
+               buf = make([]byte, 3, 3+len(i))
+               buf[0] = 0
+               buf[1] = uint8(len(i) >> 8)
+               buf[2] = uint8(len(i))
+       }
+       buf = append(buf, i...)
+       return buf
+}
+
+// Set the public key for X and Y for Curve. The two
+// values are just concatenated.
+func curveToBuf(_X, _Y *big.Int, intlen int) []byte {
+       buf := intToBytes(_X, intlen)
+       buf = append(buf, intToBytes(_Y, intlen)...)
+       return buf
+}
+
+// Set the public key for X and Y for Curve. The two
+// values are just concatenated.
+func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte {
+       t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8)
+       buf := []byte{byte(t)}
+       buf = append(buf, intToBytes(_Q, 20)...)
+       buf = append(buf, intToBytes(_P, 64+t*8)...)
+       buf = append(buf, intToBytes(_G, 64+t*8)...)
+       buf = append(buf, intToBytes(_Y, 64+t*8)...)
+       return buf
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go
new file mode 100644 (file)
index 0000000..62bb27a
--- /dev/null
@@ -0,0 +1,358 @@
+package dns
+
+import (
+       "bufio"
+       "crypto"
+       "crypto/dsa"
+       "crypto/ecdsa"
+       "crypto/rsa"
+       "io"
+       "math/big"
+       "strconv"
+       "strings"
+       "bytes"
+
+       "github.com/bytom/crypto/ed25519"
+)
+
+// NewPrivateKey returns a PrivateKey by parsing the string s.
+// s should be in the same form of the BIND private key files.
+func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {
+       if s == "" || s[len(s)-1] != '\n' { // We need a closing newline
+               return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
+       }
+       return k.ReadPrivateKey(strings.NewReader(s), "")
+}
+
+// ReadPrivateKey reads a private key from the io.Reader q. The string file is
+// only used in error reporting.
+// The public key must be known, because some cryptographic algorithms embed
+// the public inside the privatekey.
+func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
+       m, err := parseKey(q, file)
+       if m == nil {
+               return nil, err
+       }
+       if _, ok := m["private-key-format"]; !ok {
+               return nil, ErrPrivKey
+       }
+       if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
+               return nil, ErrPrivKey
+       }
+       // TODO(mg): check if the pubkey matches the private key
+       algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8)
+       if err != nil {
+               return nil, ErrPrivKey
+       }
+       switch uint8(algo) {
+       case DSA:
+               priv, err := readPrivateKeyDSA(m)
+               if err != nil {
+                       return nil, err
+               }
+               pub := k.publicKeyDSA()
+               if pub == nil {
+                       return nil, ErrKey
+               }
+               priv.PublicKey = *pub
+               return priv, nil
+       case RSAMD5:
+               fallthrough
+       case RSASHA1:
+               fallthrough
+       case RSASHA1NSEC3SHA1:
+               fallthrough
+       case RSASHA256:
+               fallthrough
+       case RSASHA512:
+               priv, err := readPrivateKeyRSA(m)
+               if err != nil {
+                       return nil, err
+               }
+               pub := k.publicKeyRSA()
+               if pub == nil {
+                       return nil, ErrKey
+               }
+               priv.PublicKey = *pub
+               return priv, nil
+       case ECCGOST:
+               return nil, ErrPrivKey
+       case ECDSAP256SHA256:
+               fallthrough
+       case ECDSAP384SHA384:
+               priv, err := readPrivateKeyECDSA(m)
+               if err != nil {
+                       return nil, err
+               }
+               pub := k.publicKeyECDSA()
+               if pub == nil {
+                       return nil, ErrKey
+               }
+               priv.PublicKey = *pub
+               return priv, nil
+       case ED25519:
+               return readPrivateKeyED25519(m)
+       default:
+               return nil, ErrPrivKey
+       }
+}
+
+// Read a private key (file) string and create a public key. Return the private key.
+func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
+       p := new(rsa.PrivateKey)
+       p.Primes = []*big.Int{nil, nil}
+       for k, v := range m {
+               switch k {
+               case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
+                       v1, err := fromBase64([]byte(v))
+                       if err != nil {
+                               return nil, err
+                       }
+                       switch k {
+                       case "modulus":
+                               p.PublicKey.N = big.NewInt(0)
+                               p.PublicKey.N.SetBytes(v1)
+                       case "publicexponent":
+                               i := big.NewInt(0)
+                               i.SetBytes(v1)
+                               p.PublicKey.E = int(i.Int64()) // int64 should be large enough
+                       case "privateexponent":
+                               p.D = big.NewInt(0)
+                               p.D.SetBytes(v1)
+                       case "prime1":
+                               p.Primes[0] = big.NewInt(0)
+                               p.Primes[0].SetBytes(v1)
+                       case "prime2":
+                               p.Primes[1] = big.NewInt(0)
+                               p.Primes[1].SetBytes(v1)
+                       }
+               case "exponent1", "exponent2", "coefficient":
+                       // not used in Go (yet)
+               case "created", "publish", "activate":
+                       // not used in Go (yet)
+               }
+       }
+       return p, nil
+}
+
+func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
+       p := new(dsa.PrivateKey)
+       p.X = big.NewInt(0)
+       for k, v := range m {
+               switch k {
+               case "private_value(x)":
+                       v1, err := fromBase64([]byte(v))
+                       if err != nil {
+                               return nil, err
+                       }
+                       p.X.SetBytes(v1)
+               case "created", "publish", "activate":
+                       /* not used in Go (yet) */
+               }
+       }
+       return p, nil
+}
+
+func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
+       p := new(ecdsa.PrivateKey)
+       p.D = big.NewInt(0)
+       // TODO: validate that the required flags are present
+       for k, v := range m {
+               switch k {
+               case "privatekey":
+                       v1, err := fromBase64([]byte(v))
+                       if err != nil {
+                               return nil, err
+                       }
+                       p.D.SetBytes(v1)
+               case "created", "publish", "activate":
+                       /* not used in Go (yet) */
+               }
+       }
+       return p, nil
+}
+
+func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
+       var p ed25519.PrivateKey
+       // TODO: validate that the required flags are present
+       for k, v := range m {
+               switch k {
+               case "privatekey":
+                       p1, err := fromBase64([]byte(v))
+                       if err != nil {
+                               return nil, err
+                       }
+                       if len(p1) != ed25519.SeedSize {
+                               return nil, ErrPrivKey
+                       }
+                       p = ed25519.NewKeyFromSeed(p1)
+               case "created", "publish", "activate":
+                       /* not used in Go (yet) */
+               }
+       }
+       return p, nil
+}
+
+// parseKey reads a private key from r. It returns a map[string]string,
+// with the key-value pairs, or an error when the file is not correct.
+func parseKey(r io.Reader, file string) (map[string]string, error) {
+       m := make(map[string]string)
+       var k string
+
+       c := newKLexer(r)
+
+       for l, ok := c.Next(); ok; l, ok = c.Next() {
+               // It should alternate
+               switch l.value {
+               case zKey:
+                       k = l.token
+               case zValue:
+                       if k == "" {
+                               return nil, &ParseError{file, "no private key seen", l}
+                       }
+
+                       m[strings.ToLower(k)] = l.token
+                       k = ""
+               }
+       }
+
+       // Surface any read errors from r.
+       if err := c.Err(); err != nil {
+               return nil, &ParseError{file: file, err: err.Error()}
+       }
+
+       return m, nil
+}
+
+type klexer struct {
+       br io.ByteReader
+
+       readErr error
+
+       line   int
+       column int
+
+       key bool
+
+       eol bool // end-of-line
+}
+
+func newKLexer(r io.Reader) *klexer {
+       br, ok := r.(io.ByteReader)
+       if !ok {
+               br = bufio.NewReaderSize(r, 1024)
+       }
+
+       return &klexer{
+               br: br,
+
+               line: 1,
+
+               key: true,
+       }
+}
+
+func (kl *klexer) Err() error {
+       if kl.readErr == io.EOF {
+               return nil
+       }
+
+       return kl.readErr
+}
+
+// readByte returns the next byte from the input
+func (kl *klexer) readByte() (byte, bool) {
+       if kl.readErr != nil {
+               return 0, false
+       }
+
+       c, err := kl.br.ReadByte()
+       if err != nil {
+               kl.readErr = err
+               return 0, false
+       }
+
+       // delay the newline handling until the next token is delivered,
+       // fixes off-by-one errors when reporting a parse error.
+       if kl.eol {
+               kl.line++
+               kl.column = 0
+               kl.eol = false
+       }
+
+       if c == '\n' {
+               kl.eol = true
+       } else {
+               kl.column++
+       }
+
+       return c, true
+}
+
+func (kl *klexer) Next() (lex, bool) {
+       var (
+               l lex
+
+               str bytes.Buffer
+
+               commt bool
+       )
+
+       for x, ok := kl.readByte(); ok; x, ok = kl.readByte() {
+               l.line, l.column = kl.line, kl.column
+
+               switch x {
+               case ':':
+                       if commt || !kl.key {
+                               break
+                       }
+
+                       kl.key = false
+
+                       // Next token is a space, eat it
+                       kl.readByte()
+
+                       l.value = zKey
+                       l.token = str.String()
+                       return l, true
+               case ';':
+                       commt = true
+               case '\n':
+                       if commt {
+                               // Reset a comment
+                               commt = false
+                       }
+
+                       if kl.key && str.Len() == 0 {
+                               // ignore empty lines
+                               break
+                       }
+
+                       kl.key = true
+
+                       l.value = zValue
+                       l.token = str.String()
+                       return l, true
+               default:
+                       if commt {
+                               break
+                       }
+
+                       str.WriteByte(x)
+               }
+       }
+
+       if kl.readErr != nil && kl.readErr != io.EOF {
+               // Don't return any tokens after a read error occurs.
+               return lex{value: zEOF}, false
+       }
+
+       if str.Len() > 0 {
+               // Send remainder
+               l.value = zValue
+               l.token = str.String()
+               return l, true
+       }
+
+       return lex{value: zEOF}, false
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go
new file mode 100644 (file)
index 0000000..c0bc0da
--- /dev/null
@@ -0,0 +1,93 @@
+package dns
+
+import (
+       "crypto"
+       "crypto/dsa"
+       "crypto/ecdsa"
+       "crypto/rsa"
+       "math/big"
+       "strconv"
+
+       "github.com/bytom/crypto/ed25519"
+)
+
+const format = "Private-key-format: v1.3\n"
+
+// PrivateKeyString converts a PrivateKey to a string. This string has the same
+// format as the private-key-file of BIND9 (Private-key-format: v1.3).
+// It needs some info from the key (the algorithm), so its a method of the DNSKEY
+// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey
+func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
+       algorithm := strconv.Itoa(int(r.Algorithm))
+       algorithm += " (" + AlgorithmToString[r.Algorithm] + ")"
+
+       switch p := p.(type) {
+       case *rsa.PrivateKey:
+               modulus := toBase64(p.PublicKey.N.Bytes())
+               e := big.NewInt(int64(p.PublicKey.E))
+               publicExponent := toBase64(e.Bytes())
+               privateExponent := toBase64(p.D.Bytes())
+               prime1 := toBase64(p.Primes[0].Bytes())
+               prime2 := toBase64(p.Primes[1].Bytes())
+               // Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
+               // and from: http://code.google.com/p/go/issues/detail?id=987
+               one := big.NewInt(1)
+               p1 := big.NewInt(0).Sub(p.Primes[0], one)
+               q1 := big.NewInt(0).Sub(p.Primes[1], one)
+               exp1 := big.NewInt(0).Mod(p.D, p1)
+               exp2 := big.NewInt(0).Mod(p.D, q1)
+               coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
+
+               exponent1 := toBase64(exp1.Bytes())
+               exponent2 := toBase64(exp2.Bytes())
+               coefficient := toBase64(coeff.Bytes())
+
+               return format +
+                       "Algorithm: " + algorithm + "\n" +
+                       "Modulus: " + modulus + "\n" +
+                       "PublicExponent: " + publicExponent + "\n" +
+                       "PrivateExponent: " + privateExponent + "\n" +
+                       "Prime1: " + prime1 + "\n" +
+                       "Prime2: " + prime2 + "\n" +
+                       "Exponent1: " + exponent1 + "\n" +
+                       "Exponent2: " + exponent2 + "\n" +
+                       "Coefficient: " + coefficient + "\n"
+
+       case *ecdsa.PrivateKey:
+               var intlen int
+               switch r.Algorithm {
+               case ECDSAP256SHA256:
+                       intlen = 32
+               case ECDSAP384SHA384:
+                       intlen = 48
+               }
+               private := toBase64(intToBytes(p.D, intlen))
+               return format +
+                       "Algorithm: " + algorithm + "\n" +
+                       "PrivateKey: " + private + "\n"
+
+       case *dsa.PrivateKey:
+               T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
+               prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
+               subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
+               base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
+               priv := toBase64(intToBytes(p.X, 20))
+               pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
+               return format +
+                       "Algorithm: " + algorithm + "\n" +
+                       "Prime(p): " + prime + "\n" +
+                       "Subprime(q): " + subprime + "\n" +
+                       "Base(g): " + base + "\n" +
+                       "Private_value(x): " + priv + "\n" +
+                       "Public_value(y): " + pub + "\n"
+
+       case ed25519.PrivateKey:
+               private := toBase64(p.Seed())
+               return format +
+                       "Algorithm: " + algorithm + "\n" +
+                       "PrivateKey: " + private + "\n"
+
+       default:
+               return ""
+       }
+}
diff --git a/vendor/github.com/miekg/dns/dnsutil/util.go b/vendor/github.com/miekg/dns/dnsutil/util.go
new file mode 100644 (file)
index 0000000..76ac4de
--- /dev/null
@@ -0,0 +1,83 @@
+// 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"
+)
+
+// AddOrigin 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"
+       // ("foo", ".") -> "foo." (Same as dns.Fqdn())
+       // ("foo.", ".") -> "foo." (Same as dns.Fqdn())
+       // ("@", "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 dns.Fqdn(s)
+       }
+
+       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 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 "@"
+       }
+       // Someone is using TrimDomainName(s, ".") to remove a dot if it exists.
+       if origin == "." {
+               return strings.TrimSuffix(s, origin)
+       }
+
+       original := s
+       s = dns.Fqdn(s)
+       origin = dns.Fqdn(origin)
+
+       if !dns.IsSubDomain(origin, s) {
+               return original
+       }
+
+       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]
+}
diff --git a/vendor/github.com/miekg/dns/dnsutil/util_test.go b/vendor/github.com/miekg/dns/dnsutil/util_test.go
new file mode 100644 (file)
index 0000000..6754789
--- /dev/null
@@ -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."},
+               {"example.com", ".", "example.com."},
+               {"example.com.", ".", "example.com."},
+               // 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, got %#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.
+       testsEx := []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 testsEx {
+                       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.
+       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 testsWild = []struct{ e1, e2, expected string }{
+               {"mathoverflow.net.", ".", "mathoverflow.net"},
+               {"mathoverflow.net", ".", "mathoverflow.net"},
+               {"", ".", "@"},
+               {"@", ".", "@"},
+       }
+       for i, test := range testsWild {
+               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)
+               }
+       }
+}
diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go
new file mode 100644 (file)
index 0000000..d3d7cec
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+Package dns implements a full featured interface to the Domain Name System.
+Both server- and client-side programming is supported. The package allows
+complete control over what is sent out to the DNS. The API follows the
+less-is-more principle, by presenting a small, clean interface.
+
+It supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
+TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
+
+Note that domain names MUST be fully qualified before sending them, unqualified
+names in a message will result in a packing failure.
+
+Resource records are native types. They are not stored in wire format. Basic
+usage pattern for creating a new resource record:
+
+     r := new(dns.MX)
+     r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}
+     r.Preference = 10
+     r.Mx = "mx.miek.nl."
+
+Or directly from a string:
+
+     mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
+
+Or when the default origin (.) and TTL (3600) and class (IN) suit you:
+
+     mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl")
+
+Or even:
+
+     mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
+
+In the DNS messages are exchanged, these messages contain resource records
+(sets). Use pattern for creating a message:
+
+     m := new(dns.Msg)
+     m.SetQuestion("miek.nl.", dns.TypeMX)
+
+Or when not certain if the domain name is fully qualified:
+
+       m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
+
+The message m is now a message with the question section set to ask the MX
+records for the miek.nl. zone.
+
+The following is slightly more verbose, but more flexible:
+
+     m1 := new(dns.Msg)
+     m1.Id = dns.Id()
+     m1.RecursionDesired = true
+     m1.Question = make([]dns.Question, 1)
+     m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
+
+After creating a message it can be sent. Basic use pattern for synchronous
+querying the DNS at a server configured on 127.0.0.1 and port 53:
+
+     c := new(dns.Client)
+     in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
+
+Suppressing multiple outstanding queries (with the same question, type and
+class) is as easy as setting:
+
+       c.SingleInflight = true
+
+More advanced options are available using a net.Dialer and the corresponding API.
+For example it is possible to set a timeout, or to specify a source IP address
+and port to use for the connection:
+
+       c := new(dns.Client)
+       laddr := net.UDPAddr{
+               IP: net.ParseIP("[::1]"),
+               Port: 12345,
+               Zone: "",
+       }
+       c.Dialer := &net.Dialer{
+               Timeout: 200 * time.Millisecond,
+               LocalAddr: &laddr,
+       }
+       in, rtt, err := c.Exchange(m1, "8.8.8.8:53")
+
+If these "advanced" features are not needed, a simple UDP query can be sent,
+with:
+
+       in, err := dns.Exchange(m1, "127.0.0.1:53")
+
+When this functions returns you will get dns message. A dns message consists
+out of four sections.
+The question section: in.Question, the answer section: in.Answer,
+the authority section: in.Ns and the additional section: in.Extra.
+
+Each of these sections (except the Question section) contain a []RR. Basic
+use pattern for accessing the rdata of a TXT RR as the first RR in
+the Answer section:
+
+       if t, ok := in.Answer[0].(*dns.TXT); ok {
+               // do something with t.Txt
+       }
+
+Domain Name and TXT Character String Representations
+
+Both domain names and TXT character strings are converted to presentation form
+both when unpacked and when converted to strings.
+
+For TXT character strings, tabs, carriage returns and line feeds will be
+converted to \t, \r and \n respectively. Back slashes and quotations marks will
+be escaped. Bytes below 32 and above 127 will be converted to \DDD form.
+
+For domain names, in addition to the above rules brackets, periods, spaces,
+semicolons and the at symbol are escaped.
+
+DNSSEC
+
+DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses
+public key cryptography to sign resource records. The public keys are stored in
+DNSKEY records and the signatures in RRSIG records.
+
+Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK)
+bit to a request.
+
+     m := new(dns.Msg)
+     m.SetEdns0(4096, true)
+
+Signature generation, signature verification and key generation are all supported.
+
+DYNAMIC UPDATES
+
+Dynamic updates reuses the DNS message format, but renames three of the
+sections. Question is Zone, Answer is Prerequisite, Authority is Update, only
+the Additional is not renamed. See RFC 2136 for the gory details.
+
+You can set a rather complex set of rules for the existence of absence of
+certain resource records or names in a zone to specify if resource records
+should be added or removed. The table from RFC 2136 supplemented with the Go
+DNS function shows which functions exist to specify the prerequisites.
+
+ 3.2.4 - Table Of Metavalues Used In Prerequisite Section
+
+  CLASS    TYPE     RDATA    Meaning                    Function
+  --------------------------------------------------------------
+  ANY      ANY      empty    Name is in use             dns.NameUsed
+  ANY      rrset    empty    RRset exists (value indep) dns.RRsetUsed
+  NONE     ANY      empty    Name is not in use         dns.NameNotUsed
+  NONE     rrset    empty    RRset does not exist       dns.RRsetNotUsed
+  zone     rrset    rr       RRset exists (value dep)   dns.Used
+
+The prerequisite section can also be left empty. If you have decided on the
+prerequisites you can tell what RRs should be added or deleted. The next table
+shows the options you have and what functions to call.
+
+ 3.4.2.6 - Table Of Metavalues Used In Update Section
+
+  CLASS    TYPE     RDATA    Meaning                     Function
+  ---------------------------------------------------------------
+  ANY      ANY      empty    Delete all RRsets from name dns.RemoveName
+  ANY      rrset    empty    Delete an RRset             dns.RemoveRRset
+  NONE     rrset    rr       Delete an RR from RRset     dns.Remove
+  zone     rrset    rr       Add to an RRset             dns.Insert
+
+TRANSACTION SIGNATURE
+
+An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
+The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512.
+
+Basic use pattern when querying with a TSIG name "axfr." (note that these key names
+must be fully qualified - as they are domain names) and the base64 secret
+"so6ZGir4GPAqINNh9U5c3A==":
+
+If an incoming message contains a TSIG record it MUST be the last record in
+the additional section (RFC2845 3.2).  This means that you should make the
+call to SetTsig last, right before executing the query.  If you make any
+changes to the RRset after calling SetTsig() the signature will be incorrect.
+
+       c := new(dns.Client)
+       c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
+       m := new(dns.Msg)
+       m.SetQuestion("miek.nl.", dns.TypeMX)
+       m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
+       ...
+       // When sending the TSIG RR is calculated and filled in before sending
+
+When requesting an zone transfer (almost all TSIG usage is when requesting zone
+transfers), with TSIG, this is the basic use pattern. In this example we
+request an AXFR for miek.nl. with TSIG key named "axfr." and secret
+"so6ZGir4GPAqINNh9U5c3A==" and using the server 176.58.119.54:
+
+       t := new(dns.Transfer)
+       m := new(dns.Msg)
+       t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
+       m.SetAxfr("miek.nl.")
+       m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
+       c, err := t.In(m, "176.58.119.54:53")
+       for r := range c { ... }
+
+You can now read the records from the transfer as they come in. Each envelope
+is checked with TSIG. If something is not correct an error is returned.
+
+Basic use pattern validating and replying to a message that has TSIG set.
+
+       server := &dns.Server{Addr: ":53", Net: "udp"}
+       server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
+       go server.ListenAndServe()
+       dns.HandleFunc(".", handleRequest)
+
+       func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
+               m := new(dns.Msg)
+               m.SetReply(r)
+               if r.IsTsig() != nil {
+                       if w.TsigStatus() == nil {
+                               // *Msg r has an TSIG record and it was validated
+                               m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
+                       } else {
+                               // *Msg r has an TSIG records and it was not valided
+                       }
+               }
+               w.WriteMsg(m)
+       }
+
+PRIVATE RRS
+
+RFC 6895 sets aside a range of type codes for private use. This range is 65,280
+- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
+can be used, before requesting an official type code from IANA.
+
+See https://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
+information.
+
+EDNS0
+
+EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by
+RFC 6891. It defines an new RR type, the OPT RR, which is then completely
+abused.
+
+Basic use pattern for creating an (empty) OPT RR:
+
+       o := new(dns.OPT)
+       o.Hdr.Name = "." // MUST be the root zone, per definition.
+       o.Hdr.Rrtype = dns.TypeOPT
+
+The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces.
+Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and
+EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note that these options
+may be combined in an OPT RR. Basic use pattern for a server to check if (and
+which) options are set:
+
+       // o is a dns.OPT
+       for _, s := range o.Option {
+               switch e := s.(type) {
+               case *dns.EDNS0_NSID:
+                       // do stuff with e.Nsid
+               case *dns.EDNS0_SUBNET:
+                       // access e.Family, e.Address, etc.
+               }
+       }
+
+SIG(0)
+
+From RFC 2931:
+
+    SIG(0) provides protection for DNS transactions and requests ....
+    ... protection for glue records, DNS requests, protection for message headers
+    on requests and responses, and protection of the overall integrity of a response.
+
+It works like TSIG, except that SIG(0) uses public key cryptography, instead of
+the shared secret approach in TSIG. Supported algorithms: DSA, ECDSAP256SHA256,
+ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512.
+
+Signing subsequent messages in multi-message sessions is not implemented.
+*/
+package dns
diff --git a/vendor/github.com/miekg/dns/duplicate.go b/vendor/github.com/miekg/dns/duplicate.go
new file mode 100644 (file)
index 0000000..00cda0a
--- /dev/null
@@ -0,0 +1,38 @@
+package dns
+
+//go:generate go run duplicate_generate.go
+
+// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL.
+// So this means the header data is equal *and* the RDATA is the same. Return true
+// is so, otherwise false.
+// It's is a protocol violation to have identical RRs in a message.
+func IsDuplicate(r1, r2 RR) bool {
+       // Check whether the record header is identical.
+       if !r1.Header().isDuplicate(r2.Header()) {
+               return false
+       }
+
+       // Check whether the RDATA is identical.
+       return r1.isDuplicate(r2)
+}
+
+func (r1 *RR_Header) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*RR_Header)
+       if !ok {
+               return false
+       }
+       if r1.Class != r2.Class {
+               return false
+       }
+       if r1.Rrtype != r2.Rrtype {
+               return false
+       }
+       if !isDuplicateName(r1.Name, r2.Name) {
+               return false
+       }
+       // ignore TTL
+       return true
+}
+
+// isDuplicateName checks if the domain names s1 and s2 are equal.
+func isDuplicateName(s1, s2 string) bool { return equal(s1, s2) }
diff --git a/vendor/github.com/miekg/dns/duplicate_generate.go b/vendor/github.com/miekg/dns/duplicate_generate.go
new file mode 100644 (file)
index 0000000..9b7a71b
--- /dev/null
@@ -0,0 +1,144 @@
+//+build ignore
+
+// types_generate.go is meant to run with go generate. It will use
+// go/{importer,types} to track down all the RR struct types. Then for each type
+// it will generate conversion tables (TypeToRR and TypeToString) and banal
+// methods (len, Header, copy) based on the struct tags. The generated source is
+// written to ztypes.go, and is meant to be checked into git.
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "go/format"
+       "go/importer"
+       "go/types"
+       "log"
+       "os"
+)
+
+var packageHdr = `
+// Code generated by "go run duplicate_generate.go"; DO NOT EDIT.
+
+package dns
+
+`
+
+func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
+       st, ok := t.Underlying().(*types.Struct)
+       if !ok {
+               return nil, false
+       }
+       if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
+               return st, false
+       }
+       if st.Field(0).Anonymous() {
+               st, _ := getTypeStruct(st.Field(0).Type(), scope)
+               return st, true
+       }
+       return nil, false
+}
+
+func main() {
+       // Import and type-check the package
+       pkg, err := importer.Default().Import("github.com/miekg/dns")
+       fatalIfErr(err)
+       scope := pkg.Scope()
+
+       // Collect actual types (*X)
+       var namedTypes []string
+       for _, name := range scope.Names() {
+               o := scope.Lookup(name)
+               if o == nil || !o.Exported() {
+                       continue
+               }
+
+               if st, _ := getTypeStruct(o.Type(), scope); st == nil {
+                       continue
+               }
+
+               if name == "PrivateRR" || name == "OPT" {
+                       continue
+               }
+
+               namedTypes = append(namedTypes, o.Name())
+       }
+
+       b := &bytes.Buffer{}
+       b.WriteString(packageHdr)
+
+       // Generate the duplicate check for each type.
+       fmt.Fprint(b, "// isDuplicate() functions\n\n")
+       for _, name := range namedTypes {
+
+               o := scope.Lookup(name)
+               st, isEmbedded := getTypeStruct(o.Type(), scope)
+               if isEmbedded {
+                       continue
+               }
+               fmt.Fprintf(b, "func (r1 *%s) isDuplicate(_r2 RR) bool {\n", name)
+               fmt.Fprintf(b, "r2, ok := _r2.(*%s)\n", name)
+               fmt.Fprint(b, "if !ok { return false }\n")
+               fmt.Fprint(b, "_ = r2\n")
+               for i := 1; i < st.NumFields(); i++ {
+                       field := st.Field(i).Name()
+                       o2 := func(s string) { fmt.Fprintf(b, s+"\n", field, field) }
+                       o3 := func(s string) { fmt.Fprintf(b, s+"\n", field, field, field) }
+
+                       // For some reason, a and aaaa don't pop up as *types.Slice here (mostly like because the are
+                       // *indirectly* defined as a slice in the net package).
+                       if _, ok := st.Field(i).Type().(*types.Slice); ok {
+                               o2("if len(r1.%s) != len(r2.%s) {\nreturn false\n}")
+
+                               if st.Tag(i) == `dns:"cdomain-name"` || st.Tag(i) == `dns:"domain-name"` {
+                                       o3(`for i := 0; i < len(r1.%s); i++ {
+                                               if !isDuplicateName(r1.%s[i], r2.%s[i]) {
+                                                       return false
+                                               }
+                                       }`)
+
+                                       continue
+                               }
+
+                               o3(`for i := 0; i < len(r1.%s); i++ {
+                                       if r1.%s[i] != r2.%s[i] {
+                                               return false
+                                       }
+                               }`)
+
+                               continue
+                       }
+
+                       switch st.Tag(i) {
+                       case `dns:"-"`:
+                               // ignored
+                       case `dns:"a"`, `dns:"aaaa"`:
+                               o2("if !r1.%s.Equal(r2.%s) {\nreturn false\n}")
+                       case `dns:"cdomain-name"`, `dns:"domain-name"`:
+                               o2("if !isDuplicateName(r1.%s, r2.%s) {\nreturn false\n}")
+                       default:
+                               o2("if r1.%s != r2.%s {\nreturn false\n}")
+                       }
+               }
+               fmt.Fprintf(b, "return true\n}\n\n")
+       }
+
+       // gofmt
+       res, err := format.Source(b.Bytes())
+       if err != nil {
+               b.WriteTo(os.Stderr)
+               log.Fatal(err)
+       }
+
+       // write result
+       f, err := os.Create("zduplicate.go")
+       fatalIfErr(err)
+       defer f.Close()
+       f.Write(res)
+}
+
+func fatalIfErr(err error) {
+       if err != nil {
+               log.Fatal(err)
+       }
+}
diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go
new file mode 100644 (file)
index 0000000..805641b
--- /dev/null
@@ -0,0 +1,659 @@
+package dns
+
+import (
+       "encoding/binary"
+       "encoding/hex"
+       "errors"
+       "fmt"
+       "net"
+       "strconv"
+)
+
+// EDNS0 Option codes.
+const (
+       EDNS0LLQ          = 0x1     // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
+       EDNS0UL           = 0x2     // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
+       EDNS0NSID         = 0x3     // nsid (See RFC 5001)
+       EDNS0DAU          = 0x5     // DNSSEC Algorithm Understood
+       EDNS0DHU          = 0x6     // DS Hash Understood
+       EDNS0N3U          = 0x7     // NSEC3 Hash Understood
+       EDNS0SUBNET       = 0x8     // client-subnet (See RFC 7871)
+       EDNS0EXPIRE       = 0x9     // EDNS0 expire
+       EDNS0COOKIE       = 0xa     // EDNS0 Cookie
+       EDNS0TCPKEEPALIVE = 0xb     // EDNS0 tcp keep alive (See RFC 7828)
+       EDNS0PADDING      = 0xc     // EDNS0 padding (See RFC 7830)
+       EDNS0LOCALSTART   = 0xFDE9  // Beginning of range reserved for local/experimental use (See RFC 6891)
+       EDNS0LOCALEND     = 0xFFFE  // End of range reserved for local/experimental use (See RFC 6891)
+       _DO               = 1 << 15 // DNSSEC OK
+)
+
+// OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
+// See RFC 6891.
+type OPT struct {
+       Hdr    RR_Header
+       Option []EDNS0 `dns:"opt"`
+}
+
+func (rr *OPT) String() string {
+       s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
+       if rr.Do() {
+               s += "flags: do; "
+       } else {
+               s += "flags: ; "
+       }
+       s += "udp: " + strconv.Itoa(int(rr.UDPSize()))
+
+       for _, o := range rr.Option {
+               switch o.(type) {
+               case *EDNS0_NSID:
+                       s += "\n; NSID: " + o.String()
+                       h, e := o.pack()
+                       var r string
+                       if e == nil {
+                               for _, c := range h {
+                                       r += "(" + string(c) + ")"
+                               }
+                               s += "  " + r
+                       }
+               case *EDNS0_SUBNET:
+                       s += "\n; SUBNET: " + o.String()
+               case *EDNS0_COOKIE:
+                       s += "\n; COOKIE: " + o.String()
+               case *EDNS0_UL:
+                       s += "\n; UPDATE LEASE: " + o.String()
+               case *EDNS0_LLQ:
+                       s += "\n; LONG LIVED QUERIES: " + o.String()
+               case *EDNS0_DAU:
+                       s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String()
+               case *EDNS0_DHU:
+                       s += "\n; DS HASH UNDERSTOOD: " + o.String()
+               case *EDNS0_N3U:
+                       s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
+               case *EDNS0_LOCAL:
+                       s += "\n; LOCAL OPT: " + o.String()
+               case *EDNS0_PADDING:
+                       s += "\n; PADDING: " + o.String()
+               }
+       }
+       return s
+}
+
+func (rr *OPT) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       for i := 0; i < len(rr.Option); i++ {
+               l += 4 // Account for 2-byte option code and 2-byte option length.
+               lo, _ := rr.Option[i].pack()
+               l += len(lo)
+       }
+       return l
+}
+
+func (rr *OPT) parse(c *zlexer, origin, file string) *ParseError {
+       panic("dns: internal error: parse should never be called on OPT")
+}
+
+func (r1 *OPT) isDuplicate(r2 RR) bool { return false }
+
+// return the old value -> delete SetVersion?
+
+// Version returns the EDNS version used. Only zero is defined.
+func (rr *OPT) Version() uint8 {
+       return uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16)
+}
+
+// SetVersion sets the version of EDNS. This is usually zero.
+func (rr *OPT) SetVersion(v uint8) {
+       rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)<<16
+}
+
+// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
+func (rr *OPT) ExtendedRcode() int {
+       return int(rr.Hdr.Ttl&0xFF000000>>24) << 4
+}
+
+// SetExtendedRcode sets the EDNS extended RCODE field.
+//
+// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0.
+func (rr *OPT) SetExtendedRcode(v uint16) {
+       rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24
+}
+
+// UDPSize returns the UDP buffer size.
+func (rr *OPT) UDPSize() uint16 {
+       return rr.Hdr.Class
+}
+
+// SetUDPSize sets the UDP buffer size.
+func (rr *OPT) SetUDPSize(size uint16) {
+       rr.Hdr.Class = size
+}
+
+// Do returns the value of the DO (DNSSEC OK) bit.
+func (rr *OPT) Do() bool {
+       return rr.Hdr.Ttl&_DO == _DO
+}
+
+// SetDo sets the DO (DNSSEC OK) bit.
+// If we pass an argument, set the DO bit to that value.
+// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored.
+func (rr *OPT) SetDo(do ...bool) {
+       if len(do) == 1 {
+               if do[0] {
+                       rr.Hdr.Ttl |= _DO
+               } else {
+                       rr.Hdr.Ttl &^= _DO
+               }
+       } else {
+               rr.Hdr.Ttl |= _DO
+       }
+}
+
+// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
+type EDNS0 interface {
+       // Option returns the option code for the option.
+       Option() uint16
+       // pack returns the bytes of the option data.
+       pack() ([]byte, error)
+       // unpack sets the data as found in the buffer. Is also sets
+       // the length of the slice as the length of the option data.
+       unpack([]byte) error
+       // String returns the string representation of the option.
+       String() string
+       // copy returns a deep-copy of the option.
+       copy() EDNS0
+}
+
+// EDNS0_NSID option is used to retrieve a nameserver
+// identifier. When sending a request Nsid must be set to the empty string
+// The identifier is an opaque string encoded as hex.
+// Basic use pattern for creating an nsid option:
+//
+//     o := new(dns.OPT)
+//     o.Hdr.Name = "."
+//     o.Hdr.Rrtype = dns.TypeOPT
+//     e := new(dns.EDNS0_NSID)
+//     e.Code = dns.EDNS0NSID
+//     e.Nsid = "AA"
+//     o.Option = append(o.Option, e)
+type EDNS0_NSID struct {
+       Code uint16 // Always EDNS0NSID
+       Nsid string // This string needs to be hex encoded
+}
+
+func (e *EDNS0_NSID) pack() ([]byte, error) {
+       h, err := hex.DecodeString(e.Nsid)
+       if err != nil {
+               return nil, err
+       }
+       return h, nil
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_NSID) Option() uint16        { return EDNS0NSID } // Option returns the option code.
+func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
+func (e *EDNS0_NSID) String() string        { return e.Nsid }
+func (e *EDNS0_NSID) copy() EDNS0           { return &EDNS0_NSID{e.Code, e.Nsid} }
+
+// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
+// an idea of where the client lives. See RFC 7871. It can then give back a different
+// answer depending on the location or network topology.
+// Basic use pattern for creating an subnet option:
+//
+//     o := new(dns.OPT)
+//     o.Hdr.Name = "."
+//     o.Hdr.Rrtype = dns.TypeOPT
+//     e := new(dns.EDNS0_SUBNET)
+//     e.Code = dns.EDNS0SUBNET
+//     e.Family = 1    // 1 for IPv4 source address, 2 for IPv6
+//     e.SourceNetmask = 32    // 32 for IPV4, 128 for IPv6
+//     e.SourceScope = 0
+//     e.Address = net.ParseIP("127.0.0.1").To4()      // for IPv4
+//     // e.Address = net.ParseIP("2001:7b8:32a::2")   // for IPV6
+//     o.Option = append(o.Option, e)
+//
+// This code will parse all the available bits when unpacking (up to optlen).
+// When packing it will apply SourceNetmask. If you need more advanced logic,
+// patches welcome and good luck.
+type EDNS0_SUBNET struct {
+       Code          uint16 // Always EDNS0SUBNET
+       Family        uint16 // 1 for IP, 2 for IP6
+       SourceNetmask uint8
+       SourceScope   uint8
+       Address       net.IP
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
+
+func (e *EDNS0_SUBNET) pack() ([]byte, error) {
+       b := make([]byte, 4)
+       binary.BigEndian.PutUint16(b[0:], e.Family)
+       b[2] = e.SourceNetmask
+       b[3] = e.SourceScope
+       switch e.Family {
+       case 0:
+               // "dig" sets AddressFamily to 0 if SourceNetmask is also 0
+               // We might don't need to complain either
+               if e.SourceNetmask != 0 {
+                       return nil, errors.New("dns: bad address family")
+               }
+       case 1:
+               if e.SourceNetmask > net.IPv4len*8 {
+                       return nil, errors.New("dns: bad netmask")
+               }
+               if len(e.Address.To4()) != net.IPv4len {
+                       return nil, errors.New("dns: bad address")
+               }
+               ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
+               needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
+               b = append(b, ip[:needLength]...)
+       case 2:
+               if e.SourceNetmask > net.IPv6len*8 {
+                       return nil, errors.New("dns: bad netmask")
+               }
+               if len(e.Address) != net.IPv6len {
+                       return nil, errors.New("dns: bad address")
+               }
+               ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
+               needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
+               b = append(b, ip[:needLength]...)
+       default:
+               return nil, errors.New("dns: bad address family")
+       }
+       return b, nil
+}
+
+func (e *EDNS0_SUBNET) unpack(b []byte) error {
+       if len(b) < 4 {
+               return ErrBuf
+       }
+       e.Family = binary.BigEndian.Uint16(b)
+       e.SourceNetmask = b[2]
+       e.SourceScope = b[3]
+       switch e.Family {
+       case 0:
+               // "dig" sets AddressFamily to 0 if SourceNetmask is also 0
+               // It's okay to accept such a packet
+               if e.SourceNetmask != 0 {
+                       return errors.New("dns: bad address family")
+               }
+               e.Address = net.IPv4(0, 0, 0, 0)
+       case 1:
+               if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
+                       return errors.New("dns: bad netmask")
+               }
+               addr := make(net.IP, net.IPv4len)
+               copy(addr, b[4:])
+               e.Address = addr.To16()
+       case 2:
+               if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
+                       return errors.New("dns: bad netmask")
+               }
+               addr := make(net.IP, net.IPv6len)
+               copy(addr, b[4:])
+               e.Address = addr
+       default:
+               return errors.New("dns: bad address family")
+       }
+       return nil
+}
+
+func (e *EDNS0_SUBNET) String() (s string) {
+       if e.Address == nil {
+               s = "<nil>"
+       } else if e.Address.To4() != nil {
+               s = e.Address.String()
+       } else {
+               s = "[" + e.Address.String() + "]"
+       }
+       s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope))
+       return
+}
+
+func (e *EDNS0_SUBNET) copy() EDNS0 {
+       return &EDNS0_SUBNET{
+               e.Code,
+               e.Family,
+               e.SourceNetmask,
+               e.SourceScope,
+               e.Address,
+       }
+}
+
+// The EDNS0_COOKIE option is used to add a DNS Cookie to a message.
+//
+//     o := new(dns.OPT)
+//     o.Hdr.Name = "."
+//     o.Hdr.Rrtype = dns.TypeOPT
+//     e := new(dns.EDNS0_COOKIE)
+//     e.Code = dns.EDNS0COOKIE
+//     e.Cookie = "24a5ac.."
+//     o.Option = append(o.Option, e)
+//
+// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is
+// always 8 bytes. It may then optionally be followed by the server cookie. The server
+// cookie is of variable length, 8 to a maximum of 32 bytes. In other words:
+//
+//     cCookie := o.Cookie[:16]
+//     sCookie := o.Cookie[16:]
+//
+// There is no guarantee that the Cookie string has a specific length.
+type EDNS0_COOKIE struct {
+       Code   uint16 // Always EDNS0COOKIE
+       Cookie string // Hex-encoded cookie data
+}
+
+func (e *EDNS0_COOKIE) pack() ([]byte, error) {
+       h, err := hex.DecodeString(e.Cookie)
+       if err != nil {
+               return nil, err
+       }
+       return h, nil
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_COOKIE) Option() uint16        { return EDNS0COOKIE }
+func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
+func (e *EDNS0_COOKIE) String() string        { return e.Cookie }
+func (e *EDNS0_COOKIE) copy() EDNS0           { return &EDNS0_COOKIE{e.Code, e.Cookie} }
+
+// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
+// an expiration on an update RR. This is helpful for clients that cannot clean
+// up after themselves. This is a draft RFC and more information can be found at
+// http://files.dns-sd.org/draft-sekar-dns-ul.txt
+//
+//     o := new(dns.OPT)
+//     o.Hdr.Name = "."
+//     o.Hdr.Rrtype = dns.TypeOPT
+//     e := new(dns.EDNS0_UL)
+//     e.Code = dns.EDNS0UL
+//     e.Lease = 120 // in seconds
+//     o.Option = append(o.Option, e)
+type EDNS0_UL struct {
+       Code  uint16 // Always EDNS0UL
+       Lease uint32
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
+func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
+func (e *EDNS0_UL) copy() EDNS0    { return &EDNS0_UL{e.Code, e.Lease} }
+
+// Copied: http://golang.org/src/pkg/net/dnsmsg.go
+func (e *EDNS0_UL) pack() ([]byte, error) {
+       b := make([]byte, 4)
+       binary.BigEndian.PutUint32(b, e.Lease)
+       return b, nil
+}
+
+func (e *EDNS0_UL) unpack(b []byte) error {
+       if len(b) < 4 {
+               return ErrBuf
+       }
+       e.Lease = binary.BigEndian.Uint32(b)
+       return nil
+}
+
+// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
+// Implemented for completeness, as the EDNS0 type code is assigned.
+type EDNS0_LLQ struct {
+       Code      uint16 // Always EDNS0LLQ
+       Version   uint16
+       Opcode    uint16
+       Error     uint16
+       Id        uint64
+       LeaseLife uint32
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
+
+func (e *EDNS0_LLQ) pack() ([]byte, error) {
+       b := make([]byte, 18)
+       binary.BigEndian.PutUint16(b[0:], e.Version)
+       binary.BigEndian.PutUint16(b[2:], e.Opcode)
+       binary.BigEndian.PutUint16(b[4:], e.Error)
+       binary.BigEndian.PutUint64(b[6:], e.Id)
+       binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
+       return b, nil
+}
+
+func (e *EDNS0_LLQ) unpack(b []byte) error {
+       if len(b) < 18 {
+               return ErrBuf
+       }
+       e.Version = binary.BigEndian.Uint16(b[0:])
+       e.Opcode = binary.BigEndian.Uint16(b[2:])
+       e.Error = binary.BigEndian.Uint16(b[4:])
+       e.Id = binary.BigEndian.Uint64(b[6:])
+       e.LeaseLife = binary.BigEndian.Uint32(b[14:])
+       return nil
+}
+
+func (e *EDNS0_LLQ) String() string {
+       s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
+               " " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(e.Id, 10) +
+               " " + strconv.FormatUint(uint64(e.LeaseLife), 10)
+       return s
+}
+func (e *EDNS0_LLQ) copy() EDNS0 {
+       return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}
+}
+
+// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975.
+type EDNS0_DAU struct {
+       Code    uint16 // Always EDNS0DAU
+       AlgCode []uint8
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_DAU) Option() uint16        { return EDNS0DAU }
+func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
+func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
+
+func (e *EDNS0_DAU) String() string {
+       s := ""
+       for i := 0; i < len(e.AlgCode); i++ {
+               if a, ok := AlgorithmToString[e.AlgCode[i]]; ok {
+                       s += " " + a
+               } else {
+                       s += " " + strconv.Itoa(int(e.AlgCode[i]))
+               }
+       }
+       return s
+}
+func (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} }
+
+// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
+type EDNS0_DHU struct {
+       Code    uint16 // Always EDNS0DHU
+       AlgCode []uint8
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_DHU) Option() uint16        { return EDNS0DHU }
+func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
+func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
+
+func (e *EDNS0_DHU) String() string {
+       s := ""
+       for i := 0; i < len(e.AlgCode); i++ {
+               if a, ok := HashToString[e.AlgCode[i]]; ok {
+                       s += " " + a
+               } else {
+                       s += " " + strconv.Itoa(int(e.AlgCode[i]))
+               }
+       }
+       return s
+}
+func (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} }
+
+// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
+type EDNS0_N3U struct {
+       Code    uint16 // Always EDNS0N3U
+       AlgCode []uint8
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_N3U) Option() uint16        { return EDNS0N3U }
+func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
+func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
+
+func (e *EDNS0_N3U) String() string {
+       // Re-use the hash map
+       s := ""
+       for i := 0; i < len(e.AlgCode); i++ {
+               if a, ok := HashToString[e.AlgCode[i]]; ok {
+                       s += " " + a
+               } else {
+                       s += " " + strconv.Itoa(int(e.AlgCode[i]))
+               }
+       }
+       return s
+}
+func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
+
+// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
+type EDNS0_EXPIRE struct {
+       Code   uint16 // Always EDNS0EXPIRE
+       Expire uint32
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
+func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
+func (e *EDNS0_EXPIRE) copy() EDNS0    { return &EDNS0_EXPIRE{e.Code, e.Expire} }
+
+func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
+       b := make([]byte, 4)
+       binary.BigEndian.PutUint32(b, e.Expire)
+       return b, nil
+}
+
+func (e *EDNS0_EXPIRE) unpack(b []byte) error {
+       if len(b) < 4 {
+               return ErrBuf
+       }
+       e.Expire = binary.BigEndian.Uint32(b)
+       return nil
+}
+
+// The EDNS0_LOCAL option is used for local/experimental purposes. The option
+// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
+// (RFC6891), although any unassigned code can actually be used.  The content of
+// the option is made available in Data, unaltered.
+// Basic use pattern for creating a local option:
+//
+//     o := new(dns.OPT)
+//     o.Hdr.Name = "."
+//     o.Hdr.Rrtype = dns.TypeOPT
+//     e := new(dns.EDNS0_LOCAL)
+//     e.Code = dns.EDNS0LOCALSTART
+//     e.Data = []byte{72, 82, 74}
+//     o.Option = append(o.Option, e)
+type EDNS0_LOCAL struct {
+       Code uint16
+       Data []byte
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
+func (e *EDNS0_LOCAL) String() string {
+       return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
+}
+func (e *EDNS0_LOCAL) copy() EDNS0 {
+       b := make([]byte, len(e.Data))
+       copy(b, e.Data)
+       return &EDNS0_LOCAL{e.Code, b}
+}
+
+func (e *EDNS0_LOCAL) pack() ([]byte, error) {
+       b := make([]byte, len(e.Data))
+       copied := copy(b, e.Data)
+       if copied != len(e.Data) {
+               return nil, ErrBuf
+       }
+       return b, nil
+}
+
+func (e *EDNS0_LOCAL) unpack(b []byte) error {
+       e.Data = make([]byte, len(b))
+       copied := copy(e.Data, b)
+       if copied != len(b) {
+               return ErrBuf
+       }
+       return nil
+}
+
+// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep
+// the TCP connection alive. See RFC 7828.
+type EDNS0_TCP_KEEPALIVE struct {
+       Code    uint16 // Always EDNSTCPKEEPALIVE
+       Length  uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
+       Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order.
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
+
+func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
+       if e.Timeout != 0 && e.Length != 2 {
+               return nil, errors.New("dns: timeout specified but length is not 2")
+       }
+       if e.Timeout == 0 && e.Length != 0 {
+               return nil, errors.New("dns: timeout not specified but length is not 0")
+       }
+       b := make([]byte, 4+e.Length)
+       binary.BigEndian.PutUint16(b[0:], e.Code)
+       binary.BigEndian.PutUint16(b[2:], e.Length)
+       if e.Length == 2 {
+               binary.BigEndian.PutUint16(b[4:], e.Timeout)
+       }
+       return b, nil
+}
+
+func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
+       if len(b) < 4 {
+               return ErrBuf
+       }
+       e.Length = binary.BigEndian.Uint16(b[2:4])
+       if e.Length != 0 && e.Length != 2 {
+               return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
+       }
+       if e.Length == 2 {
+               if len(b) < 6 {
+                       return ErrBuf
+               }
+               e.Timeout = binary.BigEndian.Uint16(b[4:6])
+       }
+       return nil
+}
+
+func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
+       s = "use tcp keep-alive"
+       if e.Length == 0 {
+               s += ", timeout omitted"
+       } else {
+               s += fmt.Sprintf(", timeout %dms", e.Timeout*100)
+       }
+       return
+}
+func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} }
+
+// EDNS0_PADDING option is used to add padding to a request/response. The default
+// value of padding SHOULD be 0x0 but other values MAY be used, for instance if
+// compression is applied before encryption which may break signatures.
+type EDNS0_PADDING struct {
+       Padding []byte
+}
+
+// Option implements the EDNS0 interface.
+func (e *EDNS0_PADDING) Option() uint16        { return EDNS0PADDING }
+func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil }
+func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil }
+func (e *EDNS0_PADDING) String() string        { return fmt.Sprintf("%0X", e.Padding) }
+func (e *EDNS0_PADDING) copy() EDNS0 {
+       b := make([]byte, len(e.Padding))
+       copy(b, e.Padding)
+       return &EDNS0_PADDING{b}
+}
diff --git a/vendor/github.com/miekg/dns/format.go b/vendor/github.com/miekg/dns/format.go
new file mode 100644 (file)
index 0000000..86057f9
--- /dev/null
@@ -0,0 +1,87 @@
+package dns
+
+import (
+       "net"
+       "reflect"
+       "strconv"
+)
+
+// NumField returns the number of rdata fields r has.
+func NumField(r RR) int {
+       return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
+}
+
+// Field returns the rdata field i as a string. Fields are indexed starting from 1.
+// RR types that holds slice data, for instance the NSEC type bitmap will return a single
+// string where the types are concatenated using a space.
+// Accessing non existing fields will cause a panic.
+func Field(r RR, i int) string {
+       if i == 0 {
+               return ""
+       }
+       d := reflect.ValueOf(r).Elem().Field(i)
+       switch d.Kind() {
+       case reflect.String:
+               return d.String()
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+               return strconv.FormatInt(d.Int(), 10)
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+               return strconv.FormatUint(d.Uint(), 10)
+       case reflect.Slice:
+               switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
+               case `dns:"a"`:
+                       // TODO(miek): Hmm store this as 16 bytes
+                       if d.Len() < net.IPv6len {
+                               return net.IPv4(byte(d.Index(0).Uint()),
+                                       byte(d.Index(1).Uint()),
+                                       byte(d.Index(2).Uint()),
+                                       byte(d.Index(3).Uint())).String()
+                       }
+                       return net.IPv4(byte(d.Index(12).Uint()),
+                               byte(d.Index(13).Uint()),
+                               byte(d.Index(14).Uint()),
+                               byte(d.Index(15).Uint())).String()
+               case `dns:"aaaa"`:
+                       return net.IP{
+                               byte(d.Index(0).Uint()),
+                               byte(d.Index(1).Uint()),
+                               byte(d.Index(2).Uint()),
+                               byte(d.Index(3).Uint()),
+                               byte(d.Index(4).Uint()),
+                               byte(d.Index(5).Uint()),
+                               byte(d.Index(6).Uint()),
+                               byte(d.Index(7).Uint()),
+                               byte(d.Index(8).Uint()),
+                               byte(d.Index(9).Uint()),
+                               byte(d.Index(10).Uint()),
+                               byte(d.Index(11).Uint()),
+                               byte(d.Index(12).Uint()),
+                               byte(d.Index(13).Uint()),
+                               byte(d.Index(14).Uint()),
+                               byte(d.Index(15).Uint()),
+                       }.String()
+               case `dns:"nsec"`:
+                       if d.Len() == 0 {
+                               return ""
+                       }
+                       s := Type(d.Index(0).Uint()).String()
+                       for i := 1; i < d.Len(); i++ {
+                               s += " " + Type(d.Index(i).Uint()).String()
+                       }
+                       return s
+               default:
+                       // if it does not have a tag its a string slice
+                       fallthrough
+               case `dns:"txt"`:
+                       if d.Len() == 0 {
+                               return ""
+                       }
+                       s := d.Index(0).String()
+                       for i := 1; i < d.Len(); i++ {
+                               s += " " + d.Index(i).String()
+                       }
+                       return s
+               }
+       }
+       return ""
+}
diff --git a/vendor/github.com/miekg/dns/fuzz.go b/vendor/github.com/miekg/dns/fuzz.go
new file mode 100644 (file)
index 0000000..a8a0918
--- /dev/null
@@ -0,0 +1,23 @@
+// +build fuzz
+
+package dns
+
+func Fuzz(data []byte) int {
+       msg := new(Msg)
+
+       if err := msg.Unpack(data); err != nil {
+               return 0
+       }
+       if _, err := msg.Pack(); err != nil {
+               return 0
+       }
+
+       return 1
+}
+
+func FuzzNewRR(data []byte) int {
+       if _, err := NewRR(string(data)); err != nil {
+               return 0
+       }
+       return 1
+}
diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go
new file mode 100644 (file)
index 0000000..97bc39f
--- /dev/null
@@ -0,0 +1,242 @@
+package dns
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "strconv"
+       "strings"
+)
+
+// Parse the $GENERATE statement as used in BIND9 zones.
+// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
+// We are called after '$GENERATE '. After which we expect:
+// * the range (12-24/2)
+// * lhs (ownername)
+// * [[ttl][class]]
+// * type
+// * rhs (rdata)
+// But we are lazy here, only the range is parsed *all* occurrences
+// of $ after that are interpreted.
+func (zp *ZoneParser) generate(l lex) (RR, bool) {
+       token := l.token
+       step := 1
+       if i := strings.IndexByte(token, '/'); i >= 0 {
+               if i+1 == len(token) {
+                       return zp.setParseError("bad step in $GENERATE range", l)
+               }
+
+               s, err := strconv.Atoi(token[i+1:])
+               if err != nil || s <= 0 {
+                       return zp.setParseError("bad step in $GENERATE range", l)
+               }
+
+               step = s
+               token = token[:i]
+       }
+
+       sx := strings.SplitN(token, "-", 2)
+       if len(sx) != 2 {
+               return zp.setParseError("bad start-stop in $GENERATE range", l)
+       }
+
+       start, err := strconv.Atoi(sx[0])
+       if err != nil {
+               return zp.setParseError("bad start in $GENERATE range", l)
+       }
+
+       end, err := strconv.Atoi(sx[1])
+       if err != nil {
+               return zp.setParseError("bad stop in $GENERATE range", l)
+       }
+       if end < 0 || start < 0 || end < start {
+               return zp.setParseError("bad range in $GENERATE range", l)
+       }
+
+       zp.c.Next() // _BLANK
+
+       // Create a complete new string, which we then parse again.
+       var s string
+       for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
+               if l.err {
+                       return zp.setParseError("bad data in $GENERATE directive", l)
+               }
+               if l.value == zNewline {
+                       break
+               }
+
+               s += l.token
+       }
+
+       r := &generateReader{
+               s: s,
+
+               cur:   start,
+               start: start,
+               end:   end,
+               step:  step,
+
+               file: zp.file,
+               lex:  &l,
+       }
+       zp.sub = NewZoneParser(r, zp.origin, zp.file)
+       zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed
+       zp.sub.SetDefaultTTL(defaultTtl)
+       return zp.subNext()
+}
+
+type generateReader struct {
+       s  string
+       si int
+
+       cur   int
+       start int
+       end   int
+       step  int
+
+       mod bytes.Buffer
+
+       escape bool
+
+       eof bool
+
+       file string
+       lex  *lex
+}
+
+func (r *generateReader) parseError(msg string, end int) *ParseError {
+       r.eof = true // Make errors sticky.
+
+       l := *r.lex
+       l.token = r.s[r.si-1 : end]
+       l.column += r.si // l.column starts one zBLANK before r.s
+
+       return &ParseError{r.file, msg, l}
+}
+
+func (r *generateReader) Read(p []byte) (int, error) {
+       // NewZLexer, through NewZoneParser, should use ReadByte and
+       // not end up here.
+
+       panic("not implemented")
+}
+
+func (r *generateReader) ReadByte() (byte, error) {
+       if r.eof {
+               return 0, io.EOF
+       }
+       if r.mod.Len() > 0 {
+               return r.mod.ReadByte()
+       }
+
+       if r.si >= len(r.s) {
+               r.si = 0
+               r.cur += r.step
+
+               r.eof = r.cur > r.end || r.cur < 0
+               return '\n', nil
+       }
+
+       si := r.si
+       r.si++
+
+       switch r.s[si] {
+       case '\\':
+               if r.escape {
+                       r.escape = false
+                       return '\\', nil
+               }
+
+               r.escape = true
+               return r.ReadByte()
+       case '$':
+               if r.escape {
+                       r.escape = false
+                       return '$', nil
+               }
+
+               mod := "%d"
+
+               if si >= len(r.s)-1 {
+                       // End of the string
+                       fmt.Fprintf(&r.mod, mod, r.cur)
+                       return r.mod.ReadByte()
+               }
+
+               if r.s[si+1] == '$' {
+                       r.si++
+                       return '$', nil
+               }
+
+               var offset int
+
+               // Search for { and }
+               if r.s[si+1] == '{' {
+                       // Modifier block
+                       sep := strings.Index(r.s[si+2:], "}")
+                       if sep < 0 {
+                               return 0, r.parseError("bad modifier in $GENERATE", len(r.s))
+                       }
+
+                       var errMsg string
+                       mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep])
+                       if errMsg != "" {
+                               return 0, r.parseError(errMsg, si+3+sep)
+                       }
+                       if r.start+offset < 0 || r.end+offset > 1<<31-1 {
+                               return 0, r.parseError("bad offset in $GENERATE", si+3+sep)
+                       }
+
+                       r.si += 2 + sep // Jump to it
+               }
+
+               fmt.Fprintf(&r.mod, mod, r.cur+offset)
+               return r.mod.ReadByte()
+       default:
+               if r.escape { // Pretty useless here
+                       r.escape = false
+                       return r.ReadByte()
+               }
+
+               return r.s[si], nil
+       }
+}
+
+// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
+func modToPrintf(s string) (string, int, string) {
+       // Modifier is { offset [ ,width [ ,base ] ] } - provide default
+       // values for optional width and type, if necessary.
+       var offStr, widthStr, base string
+       switch xs := strings.Split(s, ","); len(xs) {
+       case 1:
+               offStr, widthStr, base = xs[0], "0", "d"
+       case 2:
+               offStr, widthStr, base = xs[0], xs[1], "d"
+       case 3:
+               offStr, widthStr, base = xs[0], xs[1], xs[2]
+       default:
+               return "", 0, "bad modifier in $GENERATE"
+       }
+
+       switch base {
+       case "o", "d", "x", "X":
+       default:
+               return "", 0, "bad base in $GENERATE"
+       }
+
+       offset, err := strconv.Atoi(offStr)
+       if err != nil {
+               return "", 0, "bad offset in $GENERATE"
+       }
+
+       width, err := strconv.Atoi(widthStr)
+       if err != nil || width < 0 || width > 255 {
+               return "", 0, "bad width in $GENERATE"
+       }
+
+       if width == 0 {
+               return "%" + base, offset, ""
+       }
+
+       return "%0" + widthStr + base, offset, ""
+}
diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go
new file mode 100644 (file)
index 0000000..ca8c204
--- /dev/null
@@ -0,0 +1,190 @@
+package dns
+
+// Holds a bunch of helper functions for dealing with labels.
+
+// SplitDomainName splits a name string into it's labels.
+// www.miek.nl. returns []string{"www", "miek", "nl"}
+// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
+// The root label (.) returns nil. Note that using
+// strings.Split(s) will work in most cases, but does not handle
+// escaped dots (\.) for instance.
+// s must be a syntactically valid domain name, see IsDomainName.
+func SplitDomainName(s string) (labels []string) {
+       if len(s) == 0 {
+               return nil
+       }
+       fqdnEnd := 0 // offset of the final '.' or the length of the name
+       idx := Split(s)
+       begin := 0
+       if IsFqdn(s) {
+               fqdnEnd = len(s) - 1
+       } else {
+               fqdnEnd = len(s)
+       }
+
+       switch len(idx) {
+       case 0:
+               return nil
+       case 1:
+               // no-op
+       default:
+               end := 0
+               for i := 1; i < len(idx); i++ {
+                       end = idx[i]
+                       labels = append(labels, s[begin:end-1])
+                       begin = end
+               }
+       }
+
+       return append(labels, s[begin:fqdnEnd])
+}
+
+// CompareDomainName compares the names s1 and s2 and
+// returns how many labels they have in common starting from the *right*.
+// The comparison stops at the first inequality. The names are downcased
+// before the comparison.
+//
+// www.miek.nl. and miek.nl. have two labels in common: miek and nl
+// www.miek.nl. and www.bla.nl. have one label in common: nl
+//
+// s1 and s2 must be syntactically valid domain names.
+func CompareDomainName(s1, s2 string) (n int) {
+       // the first check: root label
+       if s1 == "." || s2 == "." {
+               return 0
+       }
+
+       l1 := Split(s1)
+       l2 := Split(s2)
+
+       j1 := len(l1) - 1 // end
+       i1 := len(l1) - 2 // start
+       j2 := len(l2) - 1
+       i2 := len(l2) - 2
+       // the second check can be done here: last/only label
+       // before we fall through into the for-loop below
+       if equal(s1[l1[j1]:], s2[l2[j2]:]) {
+               n++
+       } else {
+               return
+       }
+       for {
+               if i1 < 0 || i2 < 0 {
+                       break
+               }
+               if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) {
+                       n++
+               } else {
+                       break
+               }
+               j1--
+               i1--
+               j2--
+               i2--
+       }
+       return
+}
+
+// CountLabel counts the the number of labels in the string s.
+// s must be a syntactically valid domain name.
+func CountLabel(s string) (labels int) {
+       if s == "." {
+               return
+       }
+       off := 0
+       end := false
+       for {
+               off, end = NextLabel(s, off)
+               labels++
+               if end {
+                       return
+               }
+       }
+}
+
+// Split splits a name s into its label indexes.
+// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
+// The root name (.) returns nil. Also see SplitDomainName.
+// s must be a syntactically valid domain name.
+func Split(s string) []int {
+       if s == "." {
+               return nil
+       }
+       idx := make([]int, 1, 3)
+       off := 0
+       end := false
+
+       for {
+               off, end = NextLabel(s, off)
+               if end {
+                       return idx
+               }
+               idx = append(idx, off)
+       }
+}
+
+// NextLabel returns the index of the start of the next label in the
+// string s starting at offset.
+// The bool end is true when the end of the string has been reached.
+// Also see PrevLabel.
+func NextLabel(s string, offset int) (i int, end bool) {
+       quote := false
+       for i = offset; i < len(s)-1; i++ {
+               switch s[i] {
+               case '\\':
+                       quote = !quote
+               default:
+                       quote = false
+               case '.':
+                       if quote {
+                               quote = !quote
+                               continue
+                       }
+                       return i + 1, false
+               }
+       }
+       return i + 1, true
+}
+
+// PrevLabel returns the index of the label when starting from the right and
+// jumping n labels to the left.
+// The bool start is true when the start of the string has been overshot.
+// Also see NextLabel.
+func PrevLabel(s string, n int) (i int, start bool) {
+       if n == 0 {
+               return len(s), false
+       }
+       lab := Split(s)
+       if lab == nil {
+               return 0, true
+       }
+       if n > len(lab) {
+               return 0, true
+       }
+       return lab[len(lab)-n], false
+}
+
+// equal compares a and b while ignoring case. It returns true when equal otherwise false.
+func equal(a, b string) bool {
+       // might be lifted into API function.
+       la := len(a)
+       lb := len(b)
+       if la != lb {
+               return false
+       }
+
+       for i := la - 1; i >= 0; i-- {
+               ai := a[i]
+               bi := b[i]
+               if ai >= 'A' && ai <= 'Z' {
+                       ai |= 'a' - 'A'
+               }
+               if bi >= 'A' && bi <= 'Z' {
+                       bi |= 'a' - 'A'
+               }
+               if ai != bi {
+                       return false
+               }
+       }
+       return true
+}
diff --git a/vendor/github.com/miekg/dns/listen_go111.go b/vendor/github.com/miekg/dns/listen_go111.go
new file mode 100644 (file)
index 0000000..fad195c
--- /dev/null
@@ -0,0 +1,44 @@
+// +build go1.11
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
+
+package dns
+
+import (
+       "context"
+       "net"
+       "syscall"
+
+       "golang.org/x/sys/unix"
+)
+
+const supportsReusePort = true
+
+func reuseportControl(network, address string, c syscall.RawConn) error {
+       var opErr error
+       err := c.Control(func(fd uintptr) {
+               opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
+       })
+       if err != nil {
+               return err
+       }
+
+       return opErr
+}
+
+func listenTCP(network, addr string, reuseport bool) (net.Listener, error) {
+       var lc net.ListenConfig
+       if reuseport {
+               lc.Control = reuseportControl
+       }
+
+       return lc.Listen(context.Background(), network, addr)
+}
+
+func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) {
+       var lc net.ListenConfig
+       if reuseport {
+               lc.Control = reuseportControl
+       }
+
+       return lc.ListenPacket(context.Background(), network, addr)
+}
diff --git a/vendor/github.com/miekg/dns/listen_go_not111.go b/vendor/github.com/miekg/dns/listen_go_not111.go
new file mode 100644 (file)
index 0000000..b920141
--- /dev/null
@@ -0,0 +1,23 @@
+// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
+
+package dns
+
+import "net"
+
+const supportsReusePort = false
+
+func listenTCP(network, addr string, reuseport bool) (net.Listener, error) {
+       if reuseport {
+               // TODO(tmthrgd): return an error?
+       }
+
+       return net.Listen(network, addr)
+}
+
+func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) {
+       if reuseport {
+               // TODO(tmthrgd): return an error?
+       }
+
+       return net.ListenPacket(network, addr)
+}
diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go
new file mode 100644 (file)
index 0000000..5191fc0
--- /dev/null
@@ -0,0 +1,1241 @@
+// DNS packet assembly, see RFC 1035. Converting from - Unpack() -
+// and to - Pack() - wire format.
+// All the packers and unpackers take a (msg []byte, off int)
+// and return (off1 int, ok bool).  If they return ok==false, they
+// also return off1==len(msg), so that the next unpacker will
+// also fail.  This lets us avoid checks of ok until the end of a
+// packing sequence.
+
+package dns
+
+//go:generate go run msg_generate.go
+
+import (
+       crand "crypto/rand"
+       "encoding/binary"
+       "fmt"
+       "math/big"
+       "math/rand"
+       "strconv"
+       "strings"
+       "sync"
+)
+
+const (
+       maxCompressionOffset    = 2 << 13 // We have 14 bits for the compression pointer
+       maxDomainNameWireOctets = 255     // See RFC 1035 section 2.3.4
+
+       // This is the maximum number of compression pointers that should occur in a
+       // semantically valid message. Each label in a domain name must be at least one
+       // octet and is separated by a period. The root label won't be represented by a
+       // compression pointer to a compression pointer, hence the -2 to exclude the
+       // smallest valid root label.
+       //
+       // It is possible to construct a valid message that has more compression pointers
+       // than this, and still doesn't loop, by pointing to a previous pointer. This is
+       // not something a well written implementation should ever do, so we leave them
+       // to trip the maximum compression pointer check.
+       maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2
+
+       // This is the maximum length of a domain name in presentation format. The
+       // maximum wire length of a domain name is 255 octets (see above), with the
+       // maximum label length being 63. The wire format requires one extra byte over
+       // the presentation format, reducing the number of octets by 1. Each label in
+       // the name will be separated by a single period, with each octet in the label
+       // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum
+       // length, then the final label can only be 61 octets long to not exceed the
+       // maximum allowed wire length.
+       maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1
+)
+
+// Errors defined in this package.
+var (
+       ErrAlg           error = &Error{err: "bad algorithm"}                  // ErrAlg indicates an error with the (DNSSEC) algorithm.
+       ErrAuth          error = &Error{err: "bad authentication"}             // ErrAuth indicates an error in the TSIG authentication.
+       ErrBuf           error = &Error{err: "buffer size too small"}          // ErrBuf indicates that the buffer used is too small for the message.
+       ErrConnEmpty     error = &Error{err: "conn has no connection"}         // ErrConnEmpty indicates a connection is being used before it is initialized.
+       ErrExtendedRcode error = &Error{err: "bad extended rcode"}             // ErrExtendedRcode ...
+       ErrFqdn          error = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot.
+       ErrId            error = &Error{err: "id mismatch"}                    // ErrId indicates there is a mismatch with the message's ID.
+       ErrKeyAlg        error = &Error{err: "bad key algorithm"}              // ErrKeyAlg indicates that the algorithm in the key is not valid.
+       ErrKey           error = &Error{err: "bad key"}
+       ErrKeySize       error = &Error{err: "bad key size"}
+       ErrLongDomain    error = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)}
+       ErrNoSig         error = &Error{err: "no signature found"}
+       ErrPrivKey       error = &Error{err: "bad private key"}
+       ErrRcode         error = &Error{err: "bad rcode"}
+       ErrRdata         error = &Error{err: "bad rdata"}
+       ErrRRset         error = &Error{err: "bad rrset"}
+       ErrSecret        error = &Error{err: "no secrets defined"}
+       ErrShortRead     error = &Error{err: "short read"}
+       ErrSig           error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated.
+       ErrSoa           error = &Error{err: "no SOA"}        // ErrSOA indicates that no SOA RR was seen when doing zone transfers.
+       ErrTime          error = &Error{err: "bad time"}      // ErrTime indicates a timing error in TSIG authentication.
+)
+
+// Id by default, returns a 16 bits random number to be used as a
+// message id. The random provided should be good enough. This being a
+// variable the function can be reassigned to a custom function.
+// For instance, to make it return a static value:
+//
+//     dns.Id = func() uint16 { return 3 }
+var Id = id
+
+var (
+       idLock sync.Mutex
+       idRand *rand.Rand
+)
+
+// id returns a 16 bits random number to be used as a
+// message id. The random provided should be good enough.
+func id() uint16 {
+       idLock.Lock()
+
+       if idRand == nil {
+               // This (partially) works around
+               // https://github.com/golang/go/issues/11833 by only
+               // seeding idRand upon the first call to id.
+
+               var seed int64
+               var buf [8]byte
+
+               if _, err := crand.Read(buf[:]); err == nil {
+                       seed = int64(binary.LittleEndian.Uint64(buf[:]))
+               } else {
+                       seed = rand.Int63()
+               }
+
+               idRand = rand.New(rand.NewSource(seed))
+       }
+
+       // The call to idRand.Uint32 must be within the
+       // mutex lock because *rand.Rand is not safe for
+       // concurrent use.
+       //
+       // There is no added performance overhead to calling
+       // idRand.Uint32 inside a mutex lock over just
+       // calling rand.Uint32 as the global math/rand rng
+       // is internally protected by a sync.Mutex.
+       id := uint16(idRand.Uint32())
+
+       idLock.Unlock()
+       return id
+}
+
+// MsgHdr is a a manually-unpacked version of (id, bits).
+type MsgHdr struct {
+       Id                 uint16
+       Response           bool
+       Opcode             int
+       Authoritative      bool
+       Truncated          bool
+       RecursionDesired   bool
+       RecursionAvailable bool
+       Zero               bool
+       AuthenticatedData  bool
+       CheckingDisabled   bool
+       Rcode              int
+}
+
+// Msg contains the layout of a DNS message.
+type Msg struct {
+       MsgHdr
+       Compress bool       `json:"-"` // If true, the message will be compressed when converted to wire format.
+       Question []Question // Holds the RR(s) of the question section.
+       Answer   []RR       // Holds the RR(s) of the answer section.
+       Ns       []RR       // Holds the RR(s) of the authority section.
+       Extra    []RR       // Holds the RR(s) of the additional section.
+}
+
+// ClassToString is a maps Classes to strings for each CLASS wire type.
+var ClassToString = map[uint16]string{
+       ClassINET:   "IN",
+       ClassCSNET:  "CS",
+       ClassCHAOS:  "CH",
+       ClassHESIOD: "HS",
+       ClassNONE:   "NONE",
+       ClassANY:    "ANY",
+}
+
+// OpcodeToString maps Opcodes to strings.
+var OpcodeToString = map[int]string{
+       OpcodeQuery:  "QUERY",
+       OpcodeIQuery: "IQUERY",
+       OpcodeStatus: "STATUS",
+       OpcodeNotify: "NOTIFY",
+       OpcodeUpdate: "UPDATE",
+}
+
+// RcodeToString maps Rcodes to strings.
+var RcodeToString = map[int]string{
+       RcodeSuccess:        "NOERROR",
+       RcodeFormatError:    "FORMERR",
+       RcodeServerFailure:  "SERVFAIL",
+       RcodeNameError:      "NXDOMAIN",
+       RcodeNotImplemented: "NOTIMP",
+       RcodeRefused:        "REFUSED",
+       RcodeYXDomain:       "YXDOMAIN", // See RFC 2136
+       RcodeYXRrset:        "YXRRSET",
+       RcodeNXRrset:        "NXRRSET",
+       RcodeNotAuth:        "NOTAUTH",
+       RcodeNotZone:        "NOTZONE",
+       RcodeBadSig:         "BADSIG", // Also known as RcodeBadVers, see RFC 6891
+       //      RcodeBadVers:        "BADVERS",
+       RcodeBadKey:    "BADKEY",
+       RcodeBadTime:   "BADTIME",
+       RcodeBadMode:   "BADMODE",
+       RcodeBadName:   "BADNAME",
+       RcodeBadAlg:    "BADALG",
+       RcodeBadTrunc:  "BADTRUNC",
+       RcodeBadCookie: "BADCOOKIE",
+}
+
+// compressionMap is used to allow a more efficient compression map
+// to be used for internal packDomainName calls without changing the
+// signature or functionality of public API.
+//
+// In particular, map[string]uint16 uses 25% less per-entry memory
+// than does map[string]int.
+type compressionMap struct {
+       ext map[string]int    // external callers
+       int map[string]uint16 // internal callers
+}
+
+func (m compressionMap) valid() bool {
+       return m.int != nil || m.ext != nil
+}
+
+func (m compressionMap) insert(s string, pos int) {
+       if m.ext != nil {
+               m.ext[s] = pos
+       } else {
+               m.int[s] = uint16(pos)
+       }
+}
+
+func (m compressionMap) find(s string) (int, bool) {
+       if m.ext != nil {
+               pos, ok := m.ext[s]
+               return pos, ok
+       }
+
+       pos, ok := m.int[s]
+       return int(pos), ok
+}
+
+// Domain names are a sequence of counted strings
+// split at the dots. They end with a zero-length string.
+
+// PackDomainName packs a domain name s into msg[off:].
+// If compression is wanted compress must be true and the compression
+// map needs to hold a mapping between domain names and offsets
+// pointing into msg.
+func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
+       return packDomainName(s, msg, off, compressionMap{ext: compression}, compress)
+}
+
+func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       // XXX: A logical copy of this function exists in IsDomainName and
+       // should be kept in sync with this function.
+
+       ls := len(s)
+       if ls == 0 { // Ok, for instance when dealing with update RR without any rdata.
+               return off, nil
+       }
+
+       // If not fully qualified, error out.
+       if !IsFqdn(s) {
+               return len(msg), ErrFqdn
+       }
+
+       // Each dot ends a segment of the name.
+       // We trade each dot byte for a length byte.
+       // Except for escaped dots (\.), which are normal dots.
+       // There is also a trailing zero.
+
+       // Compression
+       pointer := -1
+
+       // Emit sequence of counted strings, chopping at dots.
+       var (
+               begin     int
+               compBegin int
+               compOff   int
+               bs        []byte
+               wasDot    bool
+       )
+loop:
+       for i := 0; i < ls; i++ {
+               var c byte
+               if bs == nil {
+                       c = s[i]
+               } else {
+                       c = bs[i]
+               }
+
+               switch c {
+               case '\\':
+                       if off+1 > len(msg) {
+                               return len(msg), ErrBuf
+                       }
+
+                       if bs == nil {
+                               bs = []byte(s)
+                       }
+
+                       // check for \DDD
+                       if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) {
+                               bs[i] = dddToByte(bs[i+1:])
+                               copy(bs[i+1:ls-3], bs[i+4:])
+                               ls -= 3
+                               compOff += 3
+                       } else {
+                               copy(bs[i:ls-1], bs[i+1:])
+                               ls--
+                               compOff++
+                       }
+
+                       wasDot = false
+               case '.':
+                       if wasDot {
+                               // two dots back to back is not legal
+                               return len(msg), ErrRdata
+                       }
+                       wasDot = true
+
+                       labelLen := i - begin
+                       if labelLen >= 1<<6 { // top two bits of length must be clear
+                               return len(msg), ErrRdata
+                       }
+
+                       // off can already (we're in a loop) be bigger than len(msg)
+                       // this happens when a name isn't fully qualified
+                       if off+1+labelLen > len(msg) {
+                               return len(msg), ErrBuf
+                       }
+
+                       // Don't try to compress '.'
+                       // We should only compress when compress is true, but we should also still pick
+                       // up names that can be used for *future* compression(s).
+                       if compression.valid() && !isRootLabel(s, bs, begin, ls) {
+                               if p, ok := compression.find(s[compBegin:]); ok {
+                                       // The first hit is the longest matching dname
+                                       // keep the pointer offset we get back and store
+                                       // the offset of the current name, because that's
+                                       // where we need to insert the pointer later
+
+                                       // If compress is true, we're allowed to compress this dname
+                                       if compress {
+                                               pointer = p // Where to point to
+                                               break loop
+                                       }
+                               } else if off < maxCompressionOffset {
+                                       // Only offsets smaller than maxCompressionOffset can be used.
+                                       compression.insert(s[compBegin:], off)
+                               }
+                       }
+
+                       // The following is covered by the length check above.
+                       msg[off] = byte(labelLen)
+
+                       if bs == nil {
+                               copy(msg[off+1:], s[begin:i])
+                       } else {
+                               copy(msg[off+1:], bs[begin:i])
+                       }
+                       off += 1 + labelLen
+
+                       begin = i + 1
+                       compBegin = begin + compOff
+               default:
+                       wasDot = false
+               }
+       }
+
+       // Root label is special
+       if isRootLabel(s, bs, 0, ls) {
+               return off, nil
+       }
+
+       // If we did compression and we find something add the pointer here
+       if pointer != -1 {
+               // We have two bytes (14 bits) to put the pointer in
+               binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000))
+               return off + 2, nil
+       }
+
+       if off < len(msg) {
+               msg[off] = 0
+       }
+
+       return off + 1, nil
+}
+
+// isRootLabel returns whether s or bs, from off to end, is the root
+// label ".".
+//
+// If bs is nil, s will be checked, otherwise bs will be checked.
+func isRootLabel(s string, bs []byte, off, end int) bool {
+       if bs == nil {
+               return s[off:end] == "."
+       }
+
+       return end-off == 1 && bs[off] == '.'
+}
+
+// Unpack a domain name.
+// In addition to the simple sequences of counted strings above,
+// domain names are allowed to refer to strings elsewhere in the
+// packet, to avoid repeating common suffixes when returning
+// many entries in a single domain.  The pointers are marked
+// by a length byte with the top two bits set.  Ignoring those
+// two bits, that byte and the next give a 14 bit offset from msg[0]
+// where we should pick up the trail.
+// Note that if we jump elsewhere in the packet,
+// we return off1 == the offset after the first pointer we found,
+// which is where the next record will start.
+// In theory, the pointers are only allowed to jump backward.
+// We let them jump anywhere and stop jumping after a while.
+
+// UnpackDomainName unpacks a domain name into a string. It returns
+// the name, the new offset into msg and any error that occurred.
+//
+// When an error is encountered, the unpacked name will be discarded
+// and len(msg) will be returned as the offset.
+func UnpackDomainName(msg []byte, off int) (string, int, error) {
+       s := make([]byte, 0, maxDomainNamePresentationLength)
+       off1 := 0
+       lenmsg := len(msg)
+       budget := maxDomainNameWireOctets
+       ptr := 0 // number of pointers followed
+Loop:
+       for {
+               if off >= lenmsg {
+                       return "", lenmsg, ErrBuf
+               }
+               c := int(msg[off])
+               off++
+               switch c & 0xC0 {
+               case 0x00:
+                       if c == 0x00 {
+                               // end of name
+                               break Loop
+                       }
+                       // literal string
+                       if off+c > lenmsg {
+                               return "", lenmsg, ErrBuf
+                       }
+                       budget -= c + 1 // +1 for the label separator
+                       if budget <= 0 {
+                               return "", lenmsg, ErrLongDomain
+                       }
+                       for j := off; j < off+c; j++ {
+                               switch b := msg[j]; b {
+                               case '.', '(', ')', ';', ' ', '@':
+                                       fallthrough
+                               case '"', '\\':
+                                       s = append(s, '\\', b)
+                               default:
+                                       if b < ' ' || b > '~' { // unprintable, use \DDD
+                                               s = append(s, escapeByte(b)...)
+                                       } else {
+                                               s = append(s, b)
+                                       }
+                               }
+                       }
+                       s = append(s, '.')
+                       off += c
+               case 0xC0:
+                       // pointer to somewhere else in msg.
+                       // remember location after first ptr,
+                       // since that's how many bytes we consumed.
+                       // also, don't follow too many pointers --
+                       // maybe there's a loop.
+                       if off >= lenmsg {
+                               return "", lenmsg, ErrBuf
+                       }
+                       c1 := msg[off]
+                       off++
+                       if ptr == 0 {
+                               off1 = off
+                       }
+                       if ptr++; ptr > maxCompressionPointers {
+                               return "", lenmsg, &Error{err: "too many compression pointers"}
+                       }
+                       // pointer should guarantee that it advances and points forwards at least
+                       // but the condition on previous three lines guarantees that it's
+                       // at least loop-free
+                       off = (c^0xC0)<<8 | int(c1)
+               default:
+                       // 0x80 and 0x40 are reserved
+                       return "", lenmsg, ErrRdata
+               }
+       }
+       if ptr == 0 {
+               off1 = off
+       }
+       if len(s) == 0 {
+               return ".", off1, nil
+       }
+       return string(s), off1, nil
+}
+
+func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) {
+       if len(txt) == 0 {
+               if offset >= len(msg) {
+                       return offset, ErrBuf
+               }
+               msg[offset] = 0
+               return offset, nil
+       }
+       var err error
+       for i := range txt {
+               if len(txt[i]) > len(tmp) {
+                       return offset, ErrBuf
+               }
+               offset, err = packTxtString(txt[i], msg, offset, tmp)
+               if err != nil {
+                       return offset, err
+               }
+       }
+       return offset, nil
+}
+
+func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) {
+       lenByteOffset := offset
+       if offset >= len(msg) || len(s) > len(tmp) {
+               return offset, ErrBuf
+       }
+       offset++
+       bs := tmp[:len(s)]
+       copy(bs, s)
+       for i := 0; i < len(bs); i++ {
+               if len(msg) <= offset {
+                       return offset, ErrBuf
+               }
+               if bs[i] == '\\' {
+                       i++
+                       if i == len(bs) {
+                               break
+                       }
+                       // check for \DDD
+                       if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
+                               msg[offset] = dddToByte(bs[i:])
+                               i += 2
+                       } else {
+                               msg[offset] = bs[i]
+                       }
+               } else {
+                       msg[offset] = bs[i]
+               }
+               offset++
+       }
+       l := offset - lenByteOffset - 1
+       if l > 255 {
+               return offset, &Error{err: "string exceeded 255 bytes in txt"}
+       }
+       msg[lenByteOffset] = byte(l)
+       return offset, nil
+}
+
+func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) {
+       if offset >= len(msg) || len(s) > len(tmp) {
+               return offset, ErrBuf
+       }
+       bs := tmp[:len(s)]
+       copy(bs, s)
+       for i := 0; i < len(bs); i++ {
+               if len(msg) <= offset {
+                       return offset, ErrBuf
+               }
+               if bs[i] == '\\' {
+                       i++
+                       if i == len(bs) {
+                               break
+                       }
+                       // check for \DDD
+                       if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
+                               msg[offset] = dddToByte(bs[i:])
+                               i += 2
+                       } else {
+                               msg[offset] = bs[i]
+                       }
+               } else {
+                       msg[offset] = bs[i]
+               }
+               offset++
+       }
+       return offset, nil
+}
+
+func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {
+       off = off0
+       var s string
+       for off < len(msg) && err == nil {
+               s, off, err = unpackString(msg, off)
+               if err == nil {
+                       ss = append(ss, s)
+               }
+       }
+       return
+}
+
+// Helpers for dealing with escaped bytes
+func isDigit(b byte) bool { return b >= '0' && b <= '9' }
+
+func dddToByte(s []byte) byte {
+       _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808
+       return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
+}
+
+func dddStringToByte(s string) byte {
+       _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808
+       return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
+}
+
+// Helper function for packing and unpacking
+func intToBytes(i *big.Int, length int) []byte {
+       buf := i.Bytes()
+       if len(buf) < length {
+               b := make([]byte, length)
+               copy(b[length-len(buf):], buf)
+               return b
+       }
+       return buf
+}
+
+// PackRR packs a resource record rr into msg[off:].
+// See PackDomainName for documentation about the compression.
+func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
+       headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress)
+       if err == nil {
+               // packRR no longer sets the Rdlength field on the rr, but
+               // callers might be expecting it so we set it here.
+               rr.Header().Rdlength = uint16(off1 - headerEnd)
+       }
+       return off1, err
+}
+
+func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) {
+       if rr == nil {
+               return len(msg), len(msg), &Error{err: "nil rr"}
+       }
+
+       headerEnd, err = rr.Header().packHeader(msg, off, compression, compress)
+       if err != nil {
+               return headerEnd, len(msg), err
+       }
+
+       off1, err = rr.pack(msg, headerEnd, compression, compress)
+       if err != nil {
+               return headerEnd, len(msg), err
+       }
+
+       rdlength := off1 - headerEnd
+       if int(uint16(rdlength)) != rdlength { // overflow
+               return headerEnd, len(msg), ErrRdata
+       }
+
+       // The RDLENGTH field is the last field in the header and we set it here.
+       binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength))
+       return headerEnd, off1, nil
+}
+
+// UnpackRR unpacks msg[off:] into an RR.
+func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
+       h, off, msg, err := unpackHeader(msg, off)
+       if err != nil {
+               return nil, len(msg), err
+       }
+
+       return UnpackRRWithHeader(h, msg, off)
+}
+
+// UnpackRRWithHeader unpacks the record type specific payload given an existing
+// RR_Header.
+func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) {
+       if newFn, ok := TypeToRR[h.Rrtype]; ok {
+               rr = newFn()
+               *rr.Header() = h
+       } else {
+               rr = &RFC3597{Hdr: h}
+       }
+
+       if noRdata(h) {
+               return rr, off, nil
+       }
+
+       end := off + int(h.Rdlength)
+
+       off, err = rr.unpack(msg, off)
+       if err != nil {
+               return nil, end, err
+       }
+       if off != end {
+               return &h, end, &Error{err: "bad rdlength"}
+       }
+
+       return rr, off, nil
+}
+
+// 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
+       // Don't pre-allocate, l may be under attacker control
+       var dst []RR
+       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
+}
+
+// Convert a MsgHdr to a string, with dig-like headers:
+//
+//;; opcode: QUERY, status: NOERROR, id: 48404
+//
+//;; flags: qr aa rd ra;
+func (h *MsgHdr) String() string {
+       if h == nil {
+               return "<nil> MsgHdr"
+       }
+
+       s := ";; opcode: " + OpcodeToString[h.Opcode]
+       s += ", status: " + RcodeToString[h.Rcode]
+       s += ", id: " + strconv.Itoa(int(h.Id)) + "\n"
+
+       s += ";; flags:"
+       if h.Response {
+               s += " qr"
+       }
+       if h.Authoritative {
+               s += " aa"
+       }
+       if h.Truncated {
+               s += " tc"
+       }
+       if h.RecursionDesired {
+               s += " rd"
+       }
+       if h.RecursionAvailable {
+               s += " ra"
+       }
+       if h.Zero { // Hmm
+               s += " z"
+       }
+       if h.AuthenticatedData {
+               s += " ad"
+       }
+       if h.CheckingDisabled {
+               s += " cd"
+       }
+
+       s += ";"
+       return s
+}
+
+// Pack packs a Msg: it is converted to to wire format.
+// If the dns.Compress is true the message will be in compressed wire format.
+func (dns *Msg) Pack() (msg []byte, err error) {
+       return dns.PackBuffer(nil)
+}
+
+// PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated.
+func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {
+       // If this message can't be compressed, avoid filling the
+       // compression map and creating garbage.
+       if dns.Compress && dns.isCompressible() {
+               compression := make(map[string]uint16) // Compression pointer mappings.
+               return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true)
+       }
+
+       return dns.packBufferWithCompressionMap(buf, compressionMap{}, false)
+}
+
+// packBufferWithCompressionMap packs a Msg, using the given buffer buf.
+func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) {
+       if dns.Rcode < 0 || dns.Rcode > 0xFFF {
+               return nil, ErrRcode
+       }
+
+       // Set extended rcode unconditionally if we have an opt, this will allow
+       // reseting the extended rcode bits if they need to.
+       if opt := dns.IsEdns0(); opt != nil {
+               opt.SetExtendedRcode(uint16(dns.Rcode))
+       } else if dns.Rcode > 0xF {
+               // If Rcode is an extended one and opt is nil, error out.
+               return nil, ErrExtendedRcode
+       }
+
+       // Convert convenient Msg into wire-like Header.
+       var dh Header
+       dh.Id = dns.Id
+       dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF)
+       if dns.Response {
+               dh.Bits |= _QR
+       }
+       if dns.Authoritative {
+               dh.Bits |= _AA
+       }
+       if dns.Truncated {
+               dh.Bits |= _TC
+       }
+       if dns.RecursionDesired {
+               dh.Bits |= _RD
+       }
+       if dns.RecursionAvailable {
+               dh.Bits |= _RA
+       }
+       if dns.Zero {
+               dh.Bits |= _Z
+       }
+       if dns.AuthenticatedData {
+               dh.Bits |= _AD
+       }
+       if dns.CheckingDisabled {
+               dh.Bits |= _CD
+       }
+
+       dh.Qdcount = uint16(len(dns.Question))
+       dh.Ancount = uint16(len(dns.Answer))
+       dh.Nscount = uint16(len(dns.Ns))
+       dh.Arcount = uint16(len(dns.Extra))
+
+       // We need the uncompressed length here, because we first pack it and then compress it.
+       msg = buf
+       uncompressedLen := msgLenWithCompressionMap(dns, nil)
+       if packLen := uncompressedLen + 1; len(msg) < packLen {
+               msg = make([]byte, packLen)
+       }
+
+       // Pack it in: header and then the pieces.
+       off := 0
+       off, err = dh.pack(msg, off, compression, compress)
+       if err != nil {
+               return nil, err
+       }
+       for _, r := range dns.Question {
+               off, err = r.pack(msg, off, compression, compress)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       for _, r := range dns.Answer {
+               _, off, err = packRR(r, msg, off, compression, compress)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       for _, r := range dns.Ns {
+               _, off, err = packRR(r, msg, off, compression, compress)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       for _, r := range dns.Extra {
+               _, off, err = packRR(r, msg, off, compression, compress)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       return msg[:off], nil
+}
+
+func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) {
+       // If we are at the end of the message we should return *just* the
+       // header. This can still be useful to the caller. 9.9.9.9 sends these
+       // when responding with REFUSED for instance.
+       if off == len(msg) {
+               // reset sections before returning
+               dns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil
+               return nil
+       }
+
+       // Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are
+       // attacker controlled. This means we can't use them to pre-allocate
+       // slices.
+       dns.Question = nil
+       for i := 0; i < int(dh.Qdcount); i++ {
+               off1 := off
+               var q Question
+               q, off, err = unpackQuestion(msg, off)
+               if err != nil {
+                       return err
+               }
+               if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
+                       dh.Qdcount = uint16(i)
+                       break
+               }
+               dns.Question = append(dns.Question, q)
+       }
+
+       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)
+       }
+       // 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))
+
+       // Set extended Rcode
+       if opt := dns.IsEdns0(); opt != nil {
+               dns.Rcode |= opt.ExtendedRcode()
+       }
+
+       if off != len(msg) {
+               // TODO(miek) make this an error?
+               // use PackOpt to let people tell how detailed the error reporting should be?
+               // println("dns: extra bytes in dns packet", off, "<", len(msg))
+       }
+       return err
+
+}
+
+// Unpack unpacks a binary message to a Msg structure.
+func (dns *Msg) Unpack(msg []byte) (err error) {
+       dh, off, err := unpackMsgHdr(msg, 0)
+       if err != nil {
+               return err
+       }
+
+       dns.setHdr(dh)
+       return dns.unpack(dh, msg, off)
+}
+
+// Convert a complete message to a string with dig-like output.
+func (dns *Msg) String() string {
+       if dns == nil {
+               return "<nil> MsgHdr"
+       }
+       s := dns.MsgHdr.String() + " "
+       s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", "
+       s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", "
+       s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", "
+       s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n"
+       if len(dns.Question) > 0 {
+               s += "\n;; QUESTION SECTION:\n"
+               for i := 0; i < len(dns.Question); i++ {
+                       s += dns.Question[i].String() + "\n"
+               }
+       }
+       if len(dns.Answer) > 0 {
+               s += "\n;; ANSWER SECTION:\n"
+               for i := 0; i < len(dns.Answer); i++ {
+                       if dns.Answer[i] != nil {
+                               s += dns.Answer[i].String() + "\n"
+                       }
+               }
+       }
+       if len(dns.Ns) > 0 {
+               s += "\n;; AUTHORITY SECTION:\n"
+               for i := 0; i < len(dns.Ns); i++ {
+                       if dns.Ns[i] != nil {
+                               s += dns.Ns[i].String() + "\n"
+                       }
+               }
+       }
+       if len(dns.Extra) > 0 {
+               s += "\n;; ADDITIONAL SECTION:\n"
+               for i := 0; i < len(dns.Extra); i++ {
+                       if dns.Extra[i] != nil {
+                               s += dns.Extra[i].String() + "\n"
+                       }
+               }
+       }
+       return s
+}
+
+// isCompressible returns whether the msg may be compressible.
+func (dns *Msg) isCompressible() bool {
+       // If we only have one question, there is nothing we can ever compress.
+       return len(dns.Question) > 1 || len(dns.Answer) > 0 ||
+               len(dns.Ns) > 0 || len(dns.Extra) > 0
+}
+
+// Len returns the message length when in (un)compressed wire format.
+// If dns.Compress is true compression it is taken into account. Len()
+// is provided to be a faster way to get the size of the resulting packet,
+// than packing it, measuring the size and discarding the buffer.
+func (dns *Msg) Len() int {
+       // If this message can't be compressed, avoid filling the
+       // compression map and creating garbage.
+       if dns.Compress && dns.isCompressible() {
+               compression := make(map[string]struct{})
+               return msgLenWithCompressionMap(dns, compression)
+       }
+
+       return msgLenWithCompressionMap(dns, nil)
+}
+
+func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int {
+       l := headerSize
+
+       for _, r := range dns.Question {
+               l += r.len(l, compression)
+       }
+       for _, r := range dns.Answer {
+               if r != nil {
+                       l += r.len(l, compression)
+               }
+       }
+       for _, r := range dns.Ns {
+               if r != nil {
+                       l += r.len(l, compression)
+               }
+       }
+       for _, r := range dns.Extra {
+               if r != nil {
+                       l += r.len(l, compression)
+               }
+       }
+
+       return l
+}
+
+func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int {
+       if s == "" || s == "." {
+               return 1
+       }
+
+       escaped := strings.Contains(s, "\\")
+
+       if compression != nil && (compress || off < maxCompressionOffset) {
+               // compressionLenSearch will insert the entry into the compression
+               // map if it doesn't contain it.
+               if l, ok := compressionLenSearch(compression, s, off); ok && compress {
+                       if escaped {
+                               return escapedNameLen(s[:l]) + 2
+                       }
+
+                       return l + 2
+               }
+       }
+
+       if escaped {
+               return escapedNameLen(s) + 1
+       }
+
+       return len(s) + 1
+}
+
+func escapedNameLen(s string) int {
+       nameLen := len(s)
+       for i := 0; i < len(s); i++ {
+               if s[i] != '\\' {
+                       continue
+               }
+
+               if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) {
+                       nameLen -= 3
+                       i += 3
+               } else {
+                       nameLen--
+                       i++
+               }
+       }
+
+       return nameLen
+}
+
+func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) {
+       for off, end := 0, false; !end; off, end = NextLabel(s, off) {
+               if _, ok := c[s[off:]]; ok {
+                       return off, true
+               }
+
+               if msgOff+off < maxCompressionOffset {
+                       c[s[off:]] = struct{}{}
+               }
+       }
+
+       return 0, false
+}
+
+// Copy returns a new RR which is a deep-copy of r.
+func Copy(r RR) RR { return r.copy() }
+
+// Len returns the length (in octets) of the uncompressed RR in wire format.
+func Len(r RR) int { return r.len(0, nil) }
+
+// Copy returns a new *Msg which is a deep-copy of dns.
+func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) }
+
+// CopyTo copies the contents to the provided message using a deep-copy and returns the copy.
+func (dns *Msg) CopyTo(r1 *Msg) *Msg {
+       r1.MsgHdr = dns.MsgHdr
+       r1.Compress = dns.Compress
+
+       if len(dns.Question) > 0 {
+               r1.Question = make([]Question, len(dns.Question))
+               copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy
+       }
+
+       rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra))
+       var rri int
+
+       if len(dns.Answer) > 0 {
+               rrbegin := rri
+               for i := 0; i < len(dns.Answer); i++ {
+                       rrArr[rri] = dns.Answer[i].copy()
+                       rri++
+               }
+               r1.Answer = rrArr[rrbegin:rri:rri]
+       }
+
+       if len(dns.Ns) > 0 {
+               rrbegin := rri
+               for i := 0; i < len(dns.Ns); i++ {
+                       rrArr[rri] = dns.Ns[i].copy()
+                       rri++
+               }
+               r1.Ns = rrArr[rrbegin:rri:rri]
+       }
+
+       if len(dns.Extra) > 0 {
+               rrbegin := rri
+               for i := 0; i < len(dns.Extra); i++ {
+                       rrArr[rri] = dns.Extra[i].copy()
+                       rri++
+               }
+               r1.Extra = rrArr[rrbegin:rri:rri]
+       }
+
+       return r1
+}
+
+func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
+       off, err := packDomainName(q.Name, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(q.Qtype, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(q.Qclass, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func unpackQuestion(msg []byte, off int) (Question, int, error) {
+       var (
+               q   Question
+               err error
+       )
+       q.Name, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return q, off, err
+       }
+       if off == len(msg) {
+               return q, off, nil
+       }
+       q.Qtype, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return q, off, err
+       }
+       if off == len(msg) {
+               return q, off, nil
+       }
+       q.Qclass, off, err = unpackUint16(msg, off)
+       if off == len(msg) {
+               return q, off, nil
+       }
+       return q, off, err
+}
+
+func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
+       off, err := packUint16(dh.Id, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(dh.Bits, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(dh.Qdcount, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(dh.Ancount, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(dh.Nscount, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(dh.Arcount, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func unpackMsgHdr(msg []byte, off int) (Header, int, error) {
+       var (
+               dh  Header
+               err error
+       )
+       dh.Id, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return dh, off, err
+       }
+       dh.Bits, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return dh, off, err
+       }
+       dh.Qdcount, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return dh, off, err
+       }
+       dh.Ancount, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return dh, off, err
+       }
+       dh.Nscount, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return dh, off, err
+       }
+       dh.Arcount, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return dh, off, err
+       }
+       return dh, off, nil
+}
+
+// setHdr set the header in the dns using the binary data in dh.
+func (dns *Msg) setHdr(dh Header) {
+       dns.Id = dh.Id
+       dns.Response = dh.Bits&_QR != 0
+       dns.Opcode = int(dh.Bits>>11) & 0xF
+       dns.Authoritative = dh.Bits&_AA != 0
+       dns.Truncated = dh.Bits&_TC != 0
+       dns.RecursionDesired = dh.Bits&_RD != 0
+       dns.RecursionAvailable = dh.Bits&_RA != 0
+       dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite.
+       dns.AuthenticatedData = dh.Bits&_AD != 0
+       dns.CheckingDisabled = dh.Bits&_CD != 0
+       dns.Rcode = int(dh.Bits & 0xF)
+}
diff --git a/vendor/github.com/miekg/dns/msg_generate.go b/vendor/github.com/miekg/dns/msg_generate.go
new file mode 100644 (file)
index 0000000..721a0fc
--- /dev/null
@@ -0,0 +1,328 @@
+//+build ignore
+
+// msg_generate.go is meant to run with go generate. It will use
+// go/{importer,types} to track down all the RR struct types. Then for each type
+// it will generate pack/unpack methods based on the struct tags. The generated source is
+// written to zmsg.go, and is meant to be checked into git.
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "go/format"
+       "go/importer"
+       "go/types"
+       "log"
+       "os"
+       "strings"
+)
+
+var packageHdr = `
+// Code generated by "go run msg_generate.go"; DO NOT EDIT.
+
+package dns
+
+`
+
+// getTypeStruct will take a type and the package scope, and return the
+// (innermost) struct if the type is considered a RR type (currently defined as
+// those structs beginning with a RR_Header, could be redefined as implementing
+// the RR interface). The bool return value indicates if embedded structs were
+// resolved.
+func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
+       st, ok := t.Underlying().(*types.Struct)
+       if !ok {
+               return nil, false
+       }
+       if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
+               return st, false
+       }
+       if st.Field(0).Anonymous() {
+               st, _ := getTypeStruct(st.Field(0).Type(), scope)
+               return st, true
+       }
+       return nil, false
+}
+
+func main() {
+       // Import and type-check the package
+       pkg, err := importer.Default().Import("github.com/miekg/dns")
+       fatalIfErr(err)
+       scope := pkg.Scope()
+
+       // Collect actual types (*X)
+       var namedTypes []string
+       for _, name := range scope.Names() {
+               o := scope.Lookup(name)
+               if o == nil || !o.Exported() {
+                       continue
+               }
+               if st, _ := getTypeStruct(o.Type(), scope); st == nil {
+                       continue
+               }
+               if name == "PrivateRR" {
+                       continue
+               }
+
+               // Check if corresponding TypeX exists
+               if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
+                       log.Fatalf("Constant Type%s does not exist.", o.Name())
+               }
+
+               namedTypes = append(namedTypes, o.Name())
+       }
+
+       b := &bytes.Buffer{}
+       b.WriteString(packageHdr)
+
+       fmt.Fprint(b, "// pack*() functions\n\n")
+       for _, name := range namedTypes {
+               o := scope.Lookup(name)
+               st, _ := getTypeStruct(o.Type(), scope)
+
+               fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n", name)
+               for i := 1; i < st.NumFields(); i++ {
+                       o := func(s string) {
+                               fmt.Fprintf(b, s, st.Field(i).Name())
+                               fmt.Fprint(b, `if err != nil {
+return off, err
+}
+`)
+                       }
+
+                       if _, ok := st.Field(i).Type().(*types.Slice); ok {
+                               switch st.Tag(i) {
+                               case `dns:"-"`: // ignored
+                               case `dns:"txt"`:
+                                       o("off, err = packStringTxt(rr.%s, msg, off)\n")
+                               case `dns:"opt"`:
+                                       o("off, err = packDataOpt(rr.%s, msg, off)\n")
+                               case `dns:"nsec"`:
+                                       o("off, err = packDataNsec(rr.%s, msg, off)\n")
+                               case `dns:"domain-name"`:
+                                       o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n")
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                               }
+                               continue
+                       }
+
+                       switch {
+                       case st.Tag(i) == `dns:"-"`: // ignored
+                       case st.Tag(i) == `dns:"cdomain-name"`:
+                               o("off, err = packDomainName(rr.%s, msg, off, compression, compress)\n")
+                       case st.Tag(i) == `dns:"domain-name"`:
+                               o("off, err = packDomainName(rr.%s, msg, off, compression, false)\n")
+                       case st.Tag(i) == `dns:"a"`:
+                               o("off, err = packDataA(rr.%s, msg, off)\n")
+                       case st.Tag(i) == `dns:"aaaa"`:
+                               o("off, err = packDataAAAA(rr.%s, msg, off)\n")
+                       case st.Tag(i) == `dns:"uint48"`:
+                               o("off, err = packUint48(rr.%s, msg, off)\n")
+                       case st.Tag(i) == `dns:"txt"`:
+                               o("off, err = packString(rr.%s, msg, off)\n")
+
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-base32`): // size-base32 can be packed just like base32
+                               fallthrough
+                       case st.Tag(i) == `dns:"base32"`:
+                               o("off, err = packStringBase32(rr.%s, msg, off)\n")
+
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): // size-base64 can be packed just like base64
+                               fallthrough
+                       case st.Tag(i) == `dns:"base64"`:
+                               o("off, err = packStringBase64(rr.%s, msg, off)\n")
+
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-hex:SaltLength`):
+                               // directly write instead of using o() so we get the error check in the correct place
+                               field := st.Field(i).Name()
+                               fmt.Fprintf(b, `// Only pack salt if value is not "-", i.e. empty
+if rr.%s != "-" {
+  off, err = packStringHex(rr.%s, msg, off)
+  if err != nil {
+    return off, err
+  }
+}
+`, field, field)
+                               continue
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-hex`): // size-hex can be packed just like hex
+                               fallthrough
+                       case st.Tag(i) == `dns:"hex"`:
+                               o("off, err = packStringHex(rr.%s, msg, off)\n")
+                       case st.Tag(i) == `dns:"any"`:
+                               o("off, err = packStringAny(rr.%s, msg, off)\n")
+                       case st.Tag(i) == `dns:"octet"`:
+                               o("off, err = packStringOctet(rr.%s, msg, off)\n")
+                       case st.Tag(i) == "":
+                               switch st.Field(i).Type().(*types.Basic).Kind() {
+                               case types.Uint8:
+                                       o("off, err = packUint8(rr.%s, msg, off)\n")
+                               case types.Uint16:
+                                       o("off, err = packUint16(rr.%s, msg, off)\n")
+                               case types.Uint32:
+                                       o("off, err = packUint32(rr.%s, msg, off)\n")
+                               case types.Uint64:
+                                       o("off, err = packUint64(rr.%s, msg, off)\n")
+                               case types.String:
+                                       o("off, err = packString(rr.%s, msg, off)\n")
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name())
+                               }
+                       default:
+                               log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                       }
+               }
+               fmt.Fprintln(b, "return off, nil }\n")
+       }
+
+       fmt.Fprint(b, "// unpack*() functions\n\n")
+       for _, name := range namedTypes {
+               o := scope.Lookup(name)
+               st, _ := getTypeStruct(o.Type(), scope)
+
+               fmt.Fprintf(b, "func (rr *%s) unpack(msg []byte, off int) (off1 int, err error) {\n", name)
+               fmt.Fprint(b, `rdStart := off
+_ = rdStart
+
+`)
+               for i := 1; i < st.NumFields(); i++ {
+                       o := func(s string) {
+                               fmt.Fprintf(b, s, st.Field(i).Name())
+                               fmt.Fprint(b, `if err != nil {
+return off, err
+}
+`)
+                       }
+
+                       // size-* are special, because they reference a struct member we should use for the length.
+                       if strings.HasPrefix(st.Tag(i), `dns:"size-`) {
+                               structMember := structMember(st.Tag(i))
+                               structTag := structTag(st.Tag(i))
+                               switch structTag {
+                               case "hex":
+                                       fmt.Fprintf(b, "rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
+                               case "base32":
+                                       fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
+                               case "base64":
+                                       fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                               }
+                               fmt.Fprint(b, `if err != nil {
+return off, err
+}
+`)
+                               continue
+                       }
+
+                       if _, ok := st.Field(i).Type().(*types.Slice); ok {
+                               switch st.Tag(i) {
+                               case `dns:"-"`: // ignored
+                               case `dns:"txt"`:
+                                       o("rr.%s, off, err = unpackStringTxt(msg, off)\n")
+                               case `dns:"opt"`:
+                                       o("rr.%s, off, err = unpackDataOpt(msg, off)\n")
+                               case `dns:"nsec"`:
+                                       o("rr.%s, off, err = unpackDataNsec(msg, off)\n")
+                               case `dns:"domain-name"`:
+                                       o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                               }
+                               continue
+                       }
+
+                       switch st.Tag(i) {
+                       case `dns:"-"`: // ignored
+                       case `dns:"cdomain-name"`:
+                               fallthrough
+                       case `dns:"domain-name"`:
+                               o("rr.%s, off, err = UnpackDomainName(msg, off)\n")
+                       case `dns:"a"`:
+                               o("rr.%s, off, err = unpackDataA(msg, off)\n")
+                       case `dns:"aaaa"`:
+                               o("rr.%s, off, err = unpackDataAAAA(msg, off)\n")
+                       case `dns:"uint48"`:
+                               o("rr.%s, off, err = unpackUint48(msg, off)\n")
+                       case `dns:"txt"`:
+                               o("rr.%s, off, err = unpackString(msg, off)\n")
+                       case `dns:"base32"`:
+                               o("rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
+                       case `dns:"base64"`:
+                               o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
+                       case `dns:"hex"`:
+                               o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
+                       case `dns:"any"`:
+                               o("rr.%s, off, err = unpackStringAny(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
+                       case `dns:"octet"`:
+                               o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
+                       case "":
+                               switch st.Field(i).Type().(*types.Basic).Kind() {
+                               case types.Uint8:
+                                       o("rr.%s, off, err = unpackUint8(msg, off)\n")
+                               case types.Uint16:
+                                       o("rr.%s, off, err = unpackUint16(msg, off)\n")
+                               case types.Uint32:
+                                       o("rr.%s, off, err = unpackUint32(msg, off)\n")
+                               case types.Uint64:
+                                       o("rr.%s, off, err = unpackUint64(msg, off)\n")
+                               case types.String:
+                                       o("rr.%s, off, err = unpackString(msg, off)\n")
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name())
+                               }
+                       default:
+                               log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                       }
+                       // If we've hit len(msg) we return without error.
+                       if i < st.NumFields()-1 {
+                               fmt.Fprintf(b, `if off == len(msg) {
+return off, nil
+       }
+`)
+                       }
+               }
+               fmt.Fprintf(b, "return off, nil }\n\n")
+       }
+
+       // gofmt
+       res, err := format.Source(b.Bytes())
+       if err != nil {
+               b.WriteTo(os.Stderr)
+               log.Fatal(err)
+       }
+
+       // write result
+       f, err := os.Create("zmsg.go")
+       fatalIfErr(err)
+       defer f.Close()
+       f.Write(res)
+}
+
+// structMember will take a tag like dns:"size-base32:SaltLength" and return the last part of this string.
+func structMember(s string) string {
+       fields := strings.Split(s, ":")
+       if len(fields) == 0 {
+               return ""
+       }
+       f := fields[len(fields)-1]
+       // f should have a closing "
+       if len(f) > 1 {
+               return f[:len(f)-1]
+       }
+       return f
+}
+
+// structTag will take a tag like dns:"size-base32:SaltLength" and return base32.
+func structTag(s string) string {
+       fields := strings.Split(s, ":")
+       if len(fields) < 2 {
+               return ""
+       }
+       return fields[1][len("\"size-"):]
+}
+
+func fatalIfErr(err error) {
+       if err != nil {
+               log.Fatal(err)
+       }
+}
diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go
new file mode 100644 (file)
index 0000000..e8ab572
--- /dev/null
@@ -0,0 +1,649 @@
+package dns
+
+import (
+       "encoding/base32"
+       "encoding/base64"
+       "encoding/binary"
+       "encoding/hex"
+       "net"
+       "bytes"
+)
+
+// helper functions called from the generated zmsg.go
+
+// These function are named after the tag to help pack/unpack, if there is no tag it is the name
+// of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or
+// packDataDomainName.
+
+func unpackDataA(msg []byte, off int) (net.IP, int, error) {
+       if off+net.IPv4len > len(msg) {
+               return nil, len(msg), &Error{err: "overflow unpacking a"}
+       }
+       a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...)
+       off += net.IPv4len
+       return a, off, nil
+}
+
+func packDataA(a net.IP, msg []byte, off int) (int, error) {
+       // It must be a slice of 4, even if it is 16, we encode only the first 4
+       if off+net.IPv4len > len(msg) {
+               return len(msg), &Error{err: "overflow packing a"}
+       }
+       switch len(a) {
+       case net.IPv4len, net.IPv6len:
+               copy(msg[off:], a.To4())
+               off += net.IPv4len
+       case 0:
+               // Allowed, for dynamic updates.
+       default:
+               return len(msg), &Error{err: "overflow packing a"}
+       }
+       return off, nil
+}
+
+func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) {
+       if off+net.IPv6len > len(msg) {
+               return nil, len(msg), &Error{err: "overflow unpacking aaaa"}
+       }
+       aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...)
+       off += net.IPv6len
+       return aaaa, off, nil
+}
+
+func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) {
+       if off+net.IPv6len > len(msg) {
+               return len(msg), &Error{err: "overflow packing aaaa"}
+       }
+
+       switch len(aaaa) {
+       case net.IPv6len:
+               copy(msg[off:], aaaa)
+               off += net.IPv6len
+       case 0:
+               // Allowed, dynamic updates.
+       default:
+               return len(msg), &Error{err: "overflow packing aaaa"}
+       }
+       return off, nil
+}
+
+// unpackHeader unpacks an RR header, returning the offset to the end of the header and a
+// re-sliced msg according to the expected length of the RR.
+func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) {
+       hdr := RR_Header{}
+       if off == len(msg) {
+               return hdr, off, msg, nil
+       }
+
+       hdr.Name, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return hdr, len(msg), msg, err
+       }
+       hdr.Rrtype, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return hdr, len(msg), msg, err
+       }
+       hdr.Class, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return hdr, len(msg), msg, err
+       }
+       hdr.Ttl, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return hdr, len(msg), msg, err
+       }
+       hdr.Rdlength, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return hdr, len(msg), msg, err
+       }
+       msg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength)
+       return hdr, off, msg, err
+}
+
+// packHeader packs an RR header, returning the offset to the end of the header.
+// See PackDomainName for documentation about the compression.
+func (hdr RR_Header) packHeader(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
+       if off == len(msg) {
+               return off, nil
+       }
+
+       off, err := packDomainName(hdr.Name, msg, off, compression, compress)
+       if err != nil {
+               return len(msg), err
+       }
+       off, err = packUint16(hdr.Rrtype, msg, off)
+       if err != nil {
+               return len(msg), err
+       }
+       off, err = packUint16(hdr.Class, msg, off)
+       if err != nil {
+               return len(msg), err
+       }
+       off, err = packUint32(hdr.Ttl, msg, off)
+       if err != nil {
+               return len(msg), err
+       }
+       off, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR.
+       if err != nil {
+               return len(msg), err
+       }
+       return off, nil
+}
+
+// helper helper functions.
+
+// truncateMsgFromRdLength truncates msg to match the expected length of the RR.
+// Returns an error if msg is smaller than the expected size.
+func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) {
+       lenrd := off + int(rdlength)
+       if lenrd > len(msg) {
+               return msg, &Error{err: "overflowing header size"}
+       }
+       return msg[:lenrd], nil
+}
+
+var base32HexNoPadEncoding = base32.HexEncoding
+
+func fromBase32(s []byte) (buf []byte, err error) {
+       for i, b := range s {
+               if b >= 'a' && b <= 'z' {
+                       s[i] = b - 32
+               }
+       }
+       buflen := base32HexNoPadEncoding.DecodedLen(len(s))
+       buf = make([]byte, buflen)
+       n, err := base32HexNoPadEncoding.Decode(buf, s)
+       buf = buf[:n]
+       return
+}
+
+func toBase32(b []byte) string {
+       return base32HexNoPadEncoding.EncodeToString(b)
+}
+
+func fromBase64(s []byte) (buf []byte, err error) {
+       buflen := base64.StdEncoding.DecodedLen(len(s))
+       buf = make([]byte, buflen)
+       n, err := base64.StdEncoding.Decode(buf, s)
+       buf = buf[:n]
+       return
+}
+
+func toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) }
+
+// dynamicUpdate returns true if the Rdlength is zero.
+func noRdata(h RR_Header) bool { return h.Rdlength == 0 }
+
+func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) {
+       if off+1 > len(msg) {
+               return 0, len(msg), &Error{err: "overflow unpacking uint8"}
+       }
+       return msg[off], off + 1, nil
+}
+
+func packUint8(i uint8, msg []byte, off int) (off1 int, err error) {
+       if off+1 > len(msg) {
+               return len(msg), &Error{err: "overflow packing uint8"}
+       }
+       msg[off] = i
+       return off + 1, nil
+}
+
+func unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) {
+       if off+2 > len(msg) {
+               return 0, len(msg), &Error{err: "overflow unpacking uint16"}
+       }
+       return binary.BigEndian.Uint16(msg[off:]), off + 2, nil
+}
+
+func packUint16(i uint16, msg []byte, off int) (off1 int, err error) {
+       if off+2 > len(msg) {
+               return len(msg), &Error{err: "overflow packing uint16"}
+       }
+       binary.BigEndian.PutUint16(msg[off:], i)
+       return off + 2, nil
+}
+
+func unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) {
+       if off+4 > len(msg) {
+               return 0, len(msg), &Error{err: "overflow unpacking uint32"}
+       }
+       return binary.BigEndian.Uint32(msg[off:]), off + 4, nil
+}
+
+func packUint32(i uint32, msg []byte, off int) (off1 int, err error) {
+       if off+4 > len(msg) {
+               return len(msg), &Error{err: "overflow packing uint32"}
+       }
+       binary.BigEndian.PutUint32(msg[off:], i)
+       return off + 4, nil
+}
+
+func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) {
+       if off+6 > len(msg) {
+               return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"}
+       }
+       // Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)
+       i = uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 |
+               uint64(msg[off+4])<<8 | uint64(msg[off+5])
+       off += 6
+       return i, off, nil
+}
+
+func packUint48(i uint64, msg []byte, off int) (off1 int, err error) {
+       if off+6 > len(msg) {
+               return len(msg), &Error{err: "overflow packing uint64 as uint48"}
+       }
+       msg[off] = byte(i >> 40)
+       msg[off+1] = byte(i >> 32)
+       msg[off+2] = byte(i >> 24)
+       msg[off+3] = byte(i >> 16)
+       msg[off+4] = byte(i >> 8)
+       msg[off+5] = byte(i)
+       off += 6
+       return off, nil
+}
+
+func unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) {
+       if off+8 > len(msg) {
+               return 0, len(msg), &Error{err: "overflow unpacking uint64"}
+       }
+       return binary.BigEndian.Uint64(msg[off:]), off + 8, nil
+}
+
+func packUint64(i uint64, msg []byte, off int) (off1 int, err error) {
+       if off+8 > len(msg) {
+               return len(msg), &Error{err: "overflow packing uint64"}
+       }
+       binary.BigEndian.PutUint64(msg[off:], i)
+       off += 8
+       return off, nil
+}
+
+func unpackString(msg []byte, off int) (string, int, error) {
+       if off+1 > len(msg) {
+               return "", off, &Error{err: "overflow unpacking txt"}
+       }
+       l := int(msg[off])
+       if off+l+1 > len(msg) {
+               return "", off, &Error{err: "overflow unpacking txt"}
+       }
+       var s bytes.Buffer
+       s.Grow(l)
+       for _, b := range msg[off+1 : off+1+l] {
+               switch {
+               case b == '"' || b == '\\':
+                       s.WriteByte('\\')
+                       s.WriteByte(b)
+               case b < ' ' || b > '~': // unprintable
+                       s.WriteString(escapeByte(b))
+               default:
+                       s.WriteByte(b)
+               }
+       }
+       off += 1 + l
+       return s.String(), off, nil
+}
+
+func packString(s string, msg []byte, off int) (int, error) {
+       txtTmp := make([]byte, 256*4+1)
+       off, err := packTxtString(s, msg, off, txtTmp)
+       if err != nil {
+               return len(msg), err
+       }
+       return off, nil
+}
+
+func unpackStringBase32(msg []byte, off, end int) (string, int, error) {
+       if end > len(msg) {
+               return "", len(msg), &Error{err: "overflow unpacking base32"}
+       }
+       s := toBase32(msg[off:end])
+       return s, end, nil
+}
+
+func packStringBase32(s string, msg []byte, off int) (int, error) {
+       b32, err := fromBase32([]byte(s))
+       if err != nil {
+               return len(msg), err
+       }
+       if off+len(b32) > len(msg) {
+               return len(msg), &Error{err: "overflow packing base32"}
+       }
+       copy(msg[off:off+len(b32)], b32)
+       off += len(b32)
+       return off, nil
+}
+
+func unpackStringBase64(msg []byte, off, end int) (string, int, error) {
+       // Rest of the RR is base64 encoded value, so we don't need an explicit length
+       // to be set. Thus far all RR's that have base64 encoded fields have those as their
+       // last one. What we do need is the end of the RR!
+       if end > len(msg) {
+               return "", len(msg), &Error{err: "overflow unpacking base64"}
+       }
+       s := toBase64(msg[off:end])
+       return s, end, nil
+}
+
+func packStringBase64(s string, msg []byte, off int) (int, error) {
+       b64, err := fromBase64([]byte(s))
+       if err != nil {
+               return len(msg), err
+       }
+       if off+len(b64) > len(msg) {
+               return len(msg), &Error{err: "overflow packing base64"}
+       }
+       copy(msg[off:off+len(b64)], b64)
+       off += len(b64)
+       return off, nil
+}
+
+func unpackStringHex(msg []byte, off, end int) (string, int, error) {
+       // Rest of the RR is hex encoded value, so we don't need an explicit length
+       // to be set. NSEC and TSIG have hex fields with a length field.
+       // What we do need is the end of the RR!
+       if end > len(msg) {
+               return "", len(msg), &Error{err: "overflow unpacking hex"}
+       }
+
+       s := hex.EncodeToString(msg[off:end])
+       return s, end, nil
+}
+
+func packStringHex(s string, msg []byte, off int) (int, error) {
+       h, err := hex.DecodeString(s)
+       if err != nil {
+               return len(msg), err
+       }
+       if off+len(h) > len(msg) {
+               return len(msg), &Error{err: "overflow packing hex"}
+       }
+       copy(msg[off:off+len(h)], h)
+       off += len(h)
+       return off, nil
+}
+
+func unpackStringAny(msg []byte, off, end int) (string, int, error) {
+       if end > len(msg) {
+               return "", len(msg), &Error{err: "overflow unpacking anything"}
+       }
+       return string(msg[off:end]), end, nil
+}
+
+func packStringAny(s string, msg []byte, off int) (int, error) {
+       if off+len(s) > len(msg) {
+               return len(msg), &Error{err: "overflow packing anything"}
+       }
+       copy(msg[off:off+len(s)], s)
+       off += len(s)
+       return off, nil
+}
+
+func unpackStringTxt(msg []byte, off int) ([]string, int, error) {
+       txt, off, err := unpackTxt(msg, off)
+       if err != nil {
+               return nil, len(msg), err
+       }
+       return txt, off, nil
+}
+
+func packStringTxt(s []string, msg []byte, off int) (int, error) {
+       txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many.
+       off, err := packTxt(s, msg, off, txtTmp)
+       if err != nil {
+               return len(msg), err
+       }
+       return off, nil
+}
+
+func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) {
+       var edns []EDNS0
+Option:
+       var code uint16
+       if off+4 > len(msg) {
+               return nil, len(msg), &Error{err: "overflow unpacking opt"}
+       }
+       code = binary.BigEndian.Uint16(msg[off:])
+       off += 2
+       optlen := binary.BigEndian.Uint16(msg[off:])
+       off += 2
+       if off+int(optlen) > len(msg) {
+               return nil, len(msg), &Error{err: "overflow unpacking opt"}
+       }
+       switch code {
+       case EDNS0NSID:
+               e := new(EDNS0_NSID)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0SUBNET:
+               e := new(EDNS0_SUBNET)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0COOKIE:
+               e := new(EDNS0_COOKIE)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0UL:
+               e := new(EDNS0_UL)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0LLQ:
+               e := new(EDNS0_LLQ)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0DAU:
+               e := new(EDNS0_DAU)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0DHU:
+               e := new(EDNS0_DHU)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0N3U:
+               e := new(EDNS0_N3U)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       case EDNS0PADDING:
+               e := new(EDNS0_PADDING)
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       default:
+               e := new(EDNS0_LOCAL)
+               e.Code = code
+               if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
+                       return nil, len(msg), err
+               }
+               edns = append(edns, e)
+               off += int(optlen)
+       }
+
+       if off < len(msg) {
+               goto Option
+       }
+
+       return edns, off, nil
+}
+
+func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
+       for _, el := range options {
+               b, err := el.pack()
+               if err != nil || off+3 > len(msg) {
+                       return len(msg), &Error{err: "overflow packing opt"}
+               }
+               binary.BigEndian.PutUint16(msg[off:], el.Option())      // Option code
+               binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length
+               off += 4
+               if off+len(b) > len(msg) {
+                       copy(msg[off:], b)
+                       off = len(msg)
+                       continue
+               }
+               // Actual data
+               copy(msg[off:off+len(b)], b)
+               off += len(b)
+       }
+       return off, nil
+}
+
+func unpackStringOctet(msg []byte, off int) (string, int, error) {
+       s := string(msg[off:])
+       return s, len(msg), nil
+}
+
+func packStringOctet(s string, msg []byte, off int) (int, error) {
+       txtTmp := make([]byte, 256*4+1)
+       off, err := packOctetString(s, msg, off, txtTmp)
+       if err != nil {
+               return len(msg), err
+       }
+       return off, nil
+}
+
+func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
+       var nsec []uint16
+       length, window, lastwindow := 0, 0, -1
+       for off < len(msg) {
+               if off+2 > len(msg) {
+                       return nsec, len(msg), &Error{err: "overflow unpacking nsecx"}
+               }
+               window = int(msg[off])
+               length = int(msg[off+1])
+               off += 2
+               if window <= lastwindow {
+                       // RFC 4034: Blocks are present in the NSEC RR RDATA in
+                       // increasing numerical order.
+                       return nsec, len(msg), &Error{err: "out of order NSEC block"}
+               }
+               if length == 0 {
+                       // RFC 4034: Blocks with no types present MUST NOT be included.
+                       return nsec, len(msg), &Error{err: "empty NSEC block"}
+               }
+               if length > 32 {
+                       return nsec, len(msg), &Error{err: "NSEC block too long"}
+               }
+               if off+length > len(msg) {
+                       return nsec, len(msg), &Error{err: "overflowing NSEC block"}
+               }
+
+               // Walk the bytes in the window and extract the type bits
+               for j := 0; j < length; j++ {
+                       b := msg[off+j]
+                       // Check the bits one by one, and set the type
+                       if b&0x80 == 0x80 {
+                               nsec = append(nsec, uint16(window*256+j*8+0))
+                       }
+                       if b&0x40 == 0x40 {
+                               nsec = append(nsec, uint16(window*256+j*8+1))
+                       }
+                       if b&0x20 == 0x20 {
+                               nsec = append(nsec, uint16(window*256+j*8+2))
+                       }
+                       if b&0x10 == 0x10 {
+                               nsec = append(nsec, uint16(window*256+j*8+3))
+                       }
+                       if b&0x8 == 0x8 {
+                               nsec = append(nsec, uint16(window*256+j*8+4))
+                       }
+                       if b&0x4 == 0x4 {
+                               nsec = append(nsec, uint16(window*256+j*8+5))
+                       }
+                       if b&0x2 == 0x2 {
+                               nsec = append(nsec, uint16(window*256+j*8+6))
+                       }
+                       if b&0x1 == 0x1 {
+                               nsec = append(nsec, uint16(window*256+j*8+7))
+                       }
+               }
+               off += length
+               lastwindow = window
+       }
+       return nsec, off, nil
+}
+
+func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) {
+       if len(bitmap) == 0 {
+               return off, nil
+       }
+       var lastwindow, lastlength uint16
+       for j := 0; j < len(bitmap); j++ {
+               t := bitmap[j]
+               window := t / 256
+               length := (t-window*256)/8 + 1
+               if window > lastwindow && lastlength != 0 { // New window, jump to the new offset
+                       off += int(lastlength) + 2
+                       lastlength = 0
+               }
+               if window < lastwindow || length < lastlength {
+                       return len(msg), &Error{err: "nsec bits out of order"}
+               }
+               if off+2+int(length) > len(msg) {
+                       return len(msg), &Error{err: "overflow packing nsec"}
+               }
+               // Setting the window #
+               msg[off] = byte(window)
+               // Setting the octets length
+               msg[off+1] = byte(length)
+               // Setting the bit value for the type in the right octet
+               msg[off+1+int(length)] |= byte(1 << (7 - t%8))
+               lastwindow, lastlength = window, length
+       }
+       off += int(lastlength) + 2
+       return off, nil
+}
+
+func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) {
+       var (
+               servers []string
+               s       string
+               err     error
+       )
+       if end > len(msg) {
+               return nil, len(msg), &Error{err: "overflow unpacking domain names"}
+       }
+       for off < end {
+               s, off, err = UnpackDomainName(msg, off)
+               if err != nil {
+                       return servers, len(msg), err
+               }
+               servers = append(servers, s)
+       }
+       return servers, off, nil
+}
+
+func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) {
+       var err error
+       for j := 0; j < len(names); j++ {
+               off, err = packDomainName(names[j], msg, off, compression, compress)
+               if err != nil {
+                       return len(msg), err
+               }
+       }
+       return off, nil
+}
diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go
new file mode 100644 (file)
index 0000000..8f071a4
--- /dev/null
@@ -0,0 +1,95 @@
+package dns
+
+import (
+       "crypto/sha1"
+       "encoding/hex"
+       "strings"
+)
+
+// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase.
+func HashName(label string, ha uint8, iter uint16, salt string) string {
+       if ha != SHA1 {
+               return ""
+       }
+
+       wireSalt := make([]byte, hex.DecodedLen(len(salt)))
+       n, err := packStringHex(salt, wireSalt, 0)
+       if err != nil {
+               return ""
+       }
+       wireSalt = wireSalt[:n]
+
+       name := make([]byte, 255)
+       off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
+       if err != nil {
+               return ""
+       }
+       name = name[:off]
+
+       s := sha1.New()
+       // k = 0
+       s.Write(name)
+       s.Write(wireSalt)
+       nsec3 := s.Sum(nil)
+
+       // k > 0
+       for k := uint16(0); k < iter; k++ {
+               s.Reset()
+               s.Write(nsec3)
+               s.Write(wireSalt)
+               nsec3 = s.Sum(nsec3[:0])
+       }
+
+       return toBase32(nsec3)
+}
+
+// Cover returns true if a name is covered by the NSEC3 record
+func (rr *NSEC3) Cover(name string) bool {
+       nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
+       owner := strings.ToUpper(rr.Hdr.Name)
+       labelIndices := Split(owner)
+       if len(labelIndices) < 2 {
+               return false
+       }
+       ownerHash := owner[:labelIndices[1]-1]
+       ownerZone := owner[labelIndices[1]:]
+       if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone
+               return false
+       }
+
+       nextHash := rr.NextDomain
+
+       // if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash
+       if ownerHash == nextHash && nameHash != ownerHash { // empty interval
+               return true
+       }
+       if ownerHash > nextHash { // end of zone
+               if nameHash > ownerHash { // covered since there is nothing after ownerHash
+                       return true
+               }
+               return nameHash < nextHash // if nameHash is before beginning of zone it is covered
+       }
+       if nameHash < ownerHash { // nameHash is before ownerHash, not covered
+               return false
+       }
+       return nameHash < nextHash // if nameHash is before nextHash is it covered (between ownerHash and nextHash)
+}
+
+// Match returns true if a name matches the NSEC3 record
+func (rr *NSEC3) Match(name string) bool {
+       nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
+       owner := strings.ToUpper(rr.Hdr.Name)
+       labelIndices := Split(owner)
+       if len(labelIndices) < 2 {
+               return false
+       }
+       ownerHash := owner[:labelIndices[1]-1]
+       ownerZone := owner[labelIndices[1]:]
+       if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone
+               return false
+       }
+       if ownerHash == nameHash {
+               return true
+       }
+       return false
+}
diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go
new file mode 100644 (file)
index 0000000..d9c0d26
--- /dev/null
@@ -0,0 +1,132 @@
+package dns
+
+import (
+       "fmt"
+       "strings"
+)
+
+// PrivateRdata is an interface used for implementing "Private Use" RR types, see
+// RFC 6895. This allows one to experiment with new RR types, without requesting an
+// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
+type PrivateRdata interface {
+       // String returns the text presentaton of the Rdata of the Private RR.
+       String() string
+       // Parse parses the Rdata of the private RR.
+       Parse([]string) error
+       // Pack is used when packing a private RR into a buffer.
+       Pack([]byte) (int, error)
+       // Unpack is used when unpacking a private RR from a buffer.
+       // TODO(miek): diff. signature than Pack, see edns0.go for instance.
+       Unpack([]byte) (int, error)
+       // Copy copies the Rdata.
+       Copy(PrivateRdata) error
+       // Len returns the length in octets of the Rdata.
+       Len() int
+}
+
+// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
+// It mocks normal RRs and implements dns.RR interface.
+type PrivateRR struct {
+       Hdr  RR_Header
+       Data PrivateRdata
+}
+
+func mkPrivateRR(rrtype uint16) *PrivateRR {
+       // Panics if RR is not an instance of PrivateRR.
+       rrfunc, ok := TypeToRR[rrtype]
+       if !ok {
+               panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
+       }
+
+       anyrr := rrfunc()
+       rr, ok := anyrr.(*PrivateRR)
+       if !ok {
+               panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
+       }
+
+       return rr
+}
+
+// Header return the RR header of r.
+func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
+
+func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
+
+// Private len and copy parts to satisfy RR interface.
+func (r *PrivateRR) len(off int, compression map[string]struct{}) int {
+       l := r.Hdr.len(off, compression)
+       l += r.Data.Len()
+       return l
+}
+
+func (r *PrivateRR) copy() RR {
+       // make new RR like this:
+       rr := mkPrivateRR(r.Hdr.Rrtype)
+       rr.Hdr = r.Hdr
+
+       err := r.Data.Copy(rr.Data)
+       if err != nil {
+               panic("dns: got value that could not be used to copy Private rdata")
+       }
+       return rr
+}
+
+func (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
+       n, err := r.Data.Pack(msg[off:])
+       if err != nil {
+               return len(msg), err
+       }
+       off += n
+       return off, nil
+}
+
+func (r *PrivateRR) unpack(msg []byte, off int) (int, error) {
+       off1, err := r.Data.Unpack(msg[off:])
+       off += off1
+       return off, err
+}
+
+func (r *PrivateRR) parse(c *zlexer, origin, file string) *ParseError {
+       var l lex
+       text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
+Fetch:
+       for {
+               // TODO(miek): we could also be returning _QUOTE, this might or might not
+               // be an issue (basically parsing TXT becomes hard)
+               switch l, _ = c.Next(); l.value {
+               case zNewline, zEOF:
+                       break Fetch
+               case zString:
+                       text = append(text, l.token)
+               }
+       }
+
+       err := r.Data.Parse(text)
+       if err != nil {
+               return &ParseError{file, err.Error(), l}
+       }
+
+       return nil
+}
+
+func (r1 *PrivateRR) isDuplicate(r2 RR) bool { return false }
+
+// PrivateHandle registers a private resource record type. It requires
+// string and numeric representation of private RR type and generator function as argument.
+func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
+       rtypestr = strings.ToUpper(rtypestr)
+
+       TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
+       TypeToString[rtype] = rtypestr
+       StringToType[rtypestr] = rtype
+}
+
+// PrivateHandleRemove removes definitions required to support private RR type.
+func PrivateHandleRemove(rtype uint16) {
+       rtypestr, ok := TypeToString[rtype]
+       if ok {
+               delete(TypeToRR, rtype)
+               delete(TypeToString, rtype)
+               delete(StringToType, rtypestr)
+       }
+}
diff --git a/vendor/github.com/miekg/dns/reverse.go b/vendor/github.com/miekg/dns/reverse.go
new file mode 100644 (file)
index 0000000..28151af
--- /dev/null
@@ -0,0 +1,52 @@
+package dns
+
+// StringToType is the reverse of TypeToString, needed for string parsing.
+var StringToType = reverseInt16(TypeToString)
+
+// StringToClass is the reverse of ClassToString, needed for string parsing.
+var StringToClass = reverseInt16(ClassToString)
+
+// StringToOpcode is a map of opcodes to strings.
+var StringToOpcode = reverseInt(OpcodeToString)
+
+// StringToRcode is a map of rcodes to strings.
+var StringToRcode = reverseInt(RcodeToString)
+
+func init() {
+       // Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733.
+       StringToRcode["NOTIMPL"] = RcodeNotImplemented
+}
+
+// StringToAlgorithm is the reverse of AlgorithmToString.
+var StringToAlgorithm = reverseInt8(AlgorithmToString)
+
+// StringToHash is a map of names to hash IDs.
+var StringToHash = reverseInt8(HashToString)
+
+// StringToCertType is the reverseof CertTypeToString.
+var StringToCertType = reverseInt16(CertTypeToString)
+
+// Reverse a map
+func reverseInt8(m map[uint8]string) map[string]uint8 {
+       n := make(map[string]uint8, len(m))
+       for u, s := range m {
+               n[s] = u
+       }
+       return n
+}
+
+func reverseInt16(m map[uint16]string) map[string]uint16 {
+       n := make(map[string]uint16, len(m))
+       for u, s := range m {
+               n[s] = u
+       }
+       return n
+}
+
+func reverseInt(m map[int]string) map[string]int {
+       n := make(map[string]int, len(m))
+       for u, s := range m {
+               n[s] = u
+       }
+       return n
+}
diff --git a/vendor/github.com/miekg/dns/sanitize.go b/vendor/github.com/miekg/dns/sanitize.go
new file mode 100644 (file)
index 0000000..a638e86
--- /dev/null
@@ -0,0 +1,86 @@
+package dns
+
+// Dedup removes identical RRs from rrs. It preserves the original ordering.
+// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
+// rrs.
+// m is used to store the RRs temporary. If it is nil a new map will be allocated.
+func Dedup(rrs []RR, m map[string]RR) []RR {
+
+       if m == nil {
+               m = make(map[string]RR)
+       }
+       // Save the keys, so we don't have to call normalizedString twice.
+       keys := make([]*string, 0, len(rrs))
+
+       for _, r := range rrs {
+               key := normalizedString(r)
+               keys = append(keys, &key)
+               if mr, ok := m[key]; ok {
+                       // Shortest TTL wins.
+                       rh, mrh := r.Header(), mr.Header()
+                       if mrh.Ttl > rh.Ttl {
+                               mrh.Ttl = rh.Ttl
+                       }
+                       continue
+               }
+
+               m[key] = r
+       }
+       // If the length of the result map equals the amount of RRs we got,
+       // it means they were all different. We can then just return the original rrset.
+       if len(m) == len(rrs) {
+               return rrs
+       }
+
+       j := 0
+       for i, r := range rrs {
+               // If keys[i] lives in the map, we should copy and remove it.
+               if _, ok := m[*keys[i]]; ok {
+                       delete(m, *keys[i])
+                       rrs[j] = r
+                       j++
+               }
+
+               if len(m) == 0 {
+                       break
+               }
+       }
+
+       return rrs[:j]
+}
+
+// normalizedString returns a normalized string from r. The TTL
+// is removed and the domain name is lowercased. We go from this:
+// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
+// lowercasename<TAB>CLASS<TAB>TYPE...
+func normalizedString(r RR) string {
+       // A string Go DNS makes has: domainname<TAB>TTL<TAB>...
+       b := []byte(r.String())
+
+       // find the first non-escaped tab, then another, so we capture where the TTL lives.
+       esc := false
+       ttlStart, ttlEnd := 0, 0
+       for i := 0; i < len(b) && ttlEnd == 0; i++ {
+               switch {
+               case b[i] == '\\':
+                       esc = !esc
+               case b[i] == '\t' && !esc:
+                       if ttlStart == 0 {
+                               ttlStart = i
+                               continue
+                       }
+                       if ttlEnd == 0 {
+                               ttlEnd = i
+                       }
+               case b[i] >= 'A' && b[i] <= 'Z' && !esc:
+                       b[i] += 32
+               default:
+                       esc = false
+               }
+       }
+
+       // remove TTL.
+       copy(b[ttlStart:], b[ttlEnd:])
+       cut := ttlEnd - ttlStart
+       return string(b[:len(b)-cut])
+}
diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go
new file mode 100644 (file)
index 0000000..a8691bc
--- /dev/null
@@ -0,0 +1,1337 @@
+package dns
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "os"
+       "path/filepath"
+       "strconv"
+       "strings"
+)
+
+const maxTok = 2048 // Largest token we can return.
+
+// The maximum depth of $INCLUDE directives supported by the
+// ZoneParser API.
+const maxIncludeDepth = 7
+
+// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
+// * Add ownernames if they are left blank;
+// * Suppress sequences of spaces;
+// * Make each RR fit on one line (_NEWLINE is send as last)
+// * Handle comments: ;
+// * Handle braces - anywhere.
+const (
+       // Zonefile
+       zEOF = iota
+       zString
+       zBlank
+       zQuote
+       zNewline
+       zRrtpe
+       zOwner
+       zClass
+       zDirOrigin   // $ORIGIN
+       zDirTTL      // $TTL
+       zDirInclude  // $INCLUDE
+       zDirGenerate // $GENERATE
+
+       // Privatekey file
+       zValue
+       zKey
+
+       zExpectOwnerDir      // Ownername
+       zExpectOwnerBl       // Whitespace after the ownername
+       zExpectAny           // Expect rrtype, ttl or class
+       zExpectAnyNoClass    // Expect rrtype or ttl
+       zExpectAnyNoClassBl  // The whitespace after _EXPECT_ANY_NOCLASS
+       zExpectAnyNoTTL      // Expect rrtype or class
+       zExpectAnyNoTTLBl    // Whitespace after _EXPECT_ANY_NOTTL
+       zExpectRrtype        // Expect rrtype
+       zExpectRrtypeBl      // Whitespace BEFORE rrtype
+       zExpectRdata         // The first element of the rdata
+       zExpectDirTTLBl      // Space after directive $TTL
+       zExpectDirTTL        // Directive $TTL
+       zExpectDirOriginBl   // Space after directive $ORIGIN
+       zExpectDirOrigin     // Directive $ORIGIN
+       zExpectDirIncludeBl  // Space after directive $INCLUDE
+       zExpectDirInclude    // Directive $INCLUDE
+       zExpectDirGenerate   // Directive $GENERATE
+       zExpectDirGenerateBl // Space after directive $GENERATE
+)
+
+// ParseError is a parsing error. It contains the parse error and the location in the io.Reader
+// where the error occurred.
+type ParseError struct {
+       file string
+       err  string
+       lex  lex
+}
+
+func (e *ParseError) Error() (s string) {
+       if e.file != "" {
+               s = e.file + ": "
+       }
+       s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " +
+               strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column)
+       return
+}
+
+type lex struct {
+       token  string // text of the token
+       err    bool   // when true, token text has lexer error
+       value  uint8  // value: zString, _BLANK, etc.
+       torc   uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
+       line   int    // line in the file
+       column int    // column in the file
+}
+
+// Token holds the token that are returned when a zone file is parsed.
+type Token struct {
+       // The scanned resource record when error is not nil.
+       RR
+       // When an error occurred, this has the error specifics.
+       Error *ParseError
+       // A potential comment positioned after the RR and on the same line.
+       Comment string
+}
+
+// ttlState describes the state necessary to fill in an omitted RR TTL
+type ttlState struct {
+       ttl           uint32 // ttl is the current default TTL
+       isByDirective bool   // isByDirective indicates whether ttl was set by a $TTL directive
+}
+
+// NewRR reads the RR contained in the string s. Only the first RR is
+// returned. If s contains no records, NewRR will return nil with no
+// error.
+//
+// The class defaults to IN and TTL defaults to 3600. The full zone
+// file syntax like $TTL, $ORIGIN, etc. is supported.
+//
+// All fields of the returned RR are set, except RR.Header().Rdlength
+// which is set to 0.
+func NewRR(s string) (RR, error) {
+       if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
+               return ReadRR(strings.NewReader(s+"\n"), "")
+       }
+       return ReadRR(strings.NewReader(s), "")
+}
+
+// ReadRR reads the RR contained in r.
+//
+// The string file is used in error reporting and to resolve relative
+// $INCLUDE directives.
+//
+// See NewRR for more documentation.
+func ReadRR(r io.Reader, file string) (RR, error) {
+       zp := NewZoneParser(r, ".", file)
+       zp.SetDefaultTTL(defaultTtl)
+       zp.SetIncludeAllowed(true)
+       rr, _ := zp.Next()
+       return rr, zp.Err()
+}
+
+// ParseZone reads a RFC 1035 style zonefile from r. It returns
+// *Tokens on the returned channel, each consisting of either a
+// parsed RR and optional comment or a nil RR and an error. The
+// channel is closed by ParseZone when the end of r is reached.
+//
+// The string file is used in error reporting and to resolve relative
+// $INCLUDE directives. The string origin is used as the initial
+// origin, as if the file would start with an $ORIGIN directive.
+//
+// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all
+// supported.
+//
+// Basic usage pattern when reading from a string (z) containing the
+// zone data:
+//
+//     for x := range dns.ParseZone(strings.NewReader(z), "", "") {
+//             if x.Error != nil {
+//                  // log.Println(x.Error)
+//              } else {
+//                  // Do something with x.RR
+//              }
+//     }
+//
+// Comments specified after an RR (and on the same line!) are
+// returned too:
+//
+//     foo. IN A 10.0.0.1 ; this is a comment
+//
+// The text "; this is comment" is returned in Token.Comment.
+// Comments inside the RR are returned concatenated along with the
+// RR. Comments on a line by themselves are discarded.
+//
+// To prevent memory leaks it is important to always fully drain the
+// returned channel. If an error occurs, it will always be the last
+// Token sent on the channel.
+//
+// Deprecated: New users should prefer the ZoneParser API.
+func ParseZone(r io.Reader, origin, file string) chan *Token {
+       t := make(chan *Token, 10000)
+       go parseZone(r, origin, file, t)
+       return t
+}
+
+func parseZone(r io.Reader, origin, file string, t chan *Token) {
+       defer close(t)
+
+       zp := NewZoneParser(r, origin, file)
+       zp.SetIncludeAllowed(true)
+
+       for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
+               t <- &Token{RR: rr, Comment: zp.Comment()}
+       }
+
+       if err := zp.Err(); err != nil {
+               pe, ok := err.(*ParseError)
+               if !ok {
+                       pe = &ParseError{file: file, err: err.Error()}
+               }
+
+               t <- &Token{Error: pe}
+       }
+}
+
+// ZoneParser is a parser for an RFC 1035 style zonefile.
+//
+// Each parsed RR in the zone is returned sequentially from Next. An
+// optional comment can be retrieved with Comment.
+//
+// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all
+// supported. Although $INCLUDE is disabled by default.
+//
+// Basic usage pattern when reading from a string (z) containing the
+// zone data:
+//
+//     zp := NewZoneParser(strings.NewReader(z), "", "")
+//
+//     for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
+//             // Do something with rr
+//     }
+//
+//     if err := zp.Err(); err != nil {
+//             // log.Println(err)
+//     }
+//
+// Comments specified after an RR (and on the same line!) are
+// returned too:
+//
+//     foo. IN A 10.0.0.1 ; this is a comment
+//
+// The text "; this is comment" is returned from Comment. Comments inside
+// the RR are returned concatenated along with the RR. Comments on a line
+// by themselves are discarded.
+type ZoneParser struct {
+       c *zlexer
+
+       parseErr *ParseError
+
+       origin string
+       file   string
+
+       defttl *ttlState
+
+       h RR_Header
+
+       // sub is used to parse $INCLUDE files and $GENERATE directives.
+       // Next, by calling subNext, forwards the resulting RRs from this
+       // sub parser to the calling code.
+       sub    *ZoneParser
+       osFile *os.File
+
+       includeDepth uint8
+
+       includeAllowed bool
+}
+
+// NewZoneParser returns an RFC 1035 style zonefile parser that reads
+// from r.
+//
+// The string file is used in error reporting and to resolve relative
+// $INCLUDE directives. The string origin is used as the initial
+// origin, as if the file would start with an $ORIGIN directive.
+func NewZoneParser(r io.Reader, origin, file string) *ZoneParser {
+       var pe *ParseError
+       if origin != "" {
+               origin = Fqdn(origin)
+               if _, ok := IsDomainName(origin); !ok {
+                       pe = &ParseError{file, "bad initial origin name", lex{}}
+               }
+       }
+
+       return &ZoneParser{
+               c: newZLexer(r),
+
+               parseErr: pe,
+
+               origin: origin,
+               file:   file,
+       }
+}
+
+// SetDefaultTTL sets the parsers default TTL to ttl.
+func (zp *ZoneParser) SetDefaultTTL(ttl uint32) {
+       zp.defttl = &ttlState{ttl, false}
+}
+
+// SetIncludeAllowed controls whether $INCLUDE directives are
+// allowed. $INCLUDE directives are not supported by default.
+//
+// The $INCLUDE directive will open and read from a user controlled
+// file on the system. Even if the file is not a valid zonefile, the
+// contents of the file may be revealed in error messages, such as:
+//
+//     /etc/passwd: dns: not a TTL: "root:x:0:0:root:/root:/bin/bash" at line: 1:31
+//     /etc/shadow: dns: not a TTL: "root:$6$<redacted>::0:99999:7:::" at line: 1:125
+func (zp *ZoneParser) SetIncludeAllowed(v bool) {
+       zp.includeAllowed = v
+}
+
+// Err returns the first non-EOF error that was encountered by the
+// ZoneParser.
+func (zp *ZoneParser) Err() error {
+       if zp.parseErr != nil {
+               return zp.parseErr
+       }
+
+       if zp.sub != nil {
+               if err := zp.sub.Err(); err != nil {
+                       return err
+               }
+       }
+
+       return zp.c.Err()
+}
+
+func (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) {
+       zp.parseErr = &ParseError{zp.file, err, l}
+       return nil, false
+}
+
+// Comment returns an optional text comment that occurred alongside
+// the RR.
+func (zp *ZoneParser) Comment() string {
+       if zp.parseErr != nil {
+               return ""
+       }
+
+       if zp.sub != nil {
+               return zp.sub.Comment()
+       }
+
+       return zp.c.Comment()
+}
+
+func (zp *ZoneParser) subNext() (RR, bool) {
+       if rr, ok := zp.sub.Next(); ok {
+               return rr, true
+       }
+
+       if zp.sub.osFile != nil {
+               zp.sub.osFile.Close()
+               zp.sub.osFile = nil
+       }
+
+       if zp.sub.Err() != nil {
+               // We have errors to surface.
+               return nil, false
+       }
+
+       zp.sub = nil
+       return zp.Next()
+}
+
+// Next advances the parser to the next RR in the zonefile and
+// returns the (RR, true). It will return (nil, false) when the
+// parsing stops, either by reaching the end of the input or an
+// error. After Next returns (nil, false), the Err method will return
+// any error that occurred during parsing.
+func (zp *ZoneParser) Next() (RR, bool) {
+       if zp.parseErr != nil {
+               return nil, false
+       }
+       if zp.sub != nil {
+               return zp.subNext()
+       }
+
+       // 6 possible beginnings of a line (_ is a space):
+       //
+       //   0. zRRTYPE                              -> all omitted until the rrtype
+       //   1. zOwner _ zRrtype                     -> class/ttl omitted
+       //   2. zOwner _ zString _ zRrtype           -> class omitted
+       //   3. zOwner _ zString _ zClass  _ zRrtype -> ttl/class
+       //   4. zOwner _ zClass  _ zRrtype           -> ttl omitted
+       //   5. zOwner _ zClass  _ zString _ zRrtype -> class/ttl (reversed)
+       //
+       // After detecting these, we know the zRrtype so we can jump to functions
+       // handling the rdata for each of these types.
+
+       st := zExpectOwnerDir // initial state
+       h := &zp.h
+
+       for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
+               // zlexer spotted an error already
+               if l.err {
+                       return zp.setParseError(l.token, l)
+               }
+
+               switch st {
+               case zExpectOwnerDir:
+                       // We can also expect a directive, like $TTL or $ORIGIN
+                       if zp.defttl != nil {
+                               h.Ttl = zp.defttl.ttl
+                       }
+
+                       h.Class = ClassINET
+
+                       switch l.value {
+                       case zNewline:
+                               st = zExpectOwnerDir
+                       case zOwner:
+                               name, ok := toAbsoluteName(l.token, zp.origin)
+                               if !ok {
+                                       return zp.setParseError("bad owner name", l)
+                               }
+
+                               h.Name = name
+
+                               st = zExpectOwnerBl
+                       case zDirTTL:
+                               st = zExpectDirTTLBl
+                       case zDirOrigin:
+                               st = zExpectDirOriginBl
+                       case zDirInclude:
+                               st = zExpectDirIncludeBl
+                       case zDirGenerate:
+                               st = zExpectDirGenerateBl
+                       case zRrtpe:
+                               h.Rrtype = l.torc
+
+                               st = zExpectRdata
+                       case zClass:
+                               h.Class = l.torc
+
+                               st = zExpectAnyNoClassBl
+                       case zBlank:
+                               // Discard, can happen when there is nothing on the
+                               // line except the RR type
+                       case zString:
+                               ttl, ok := stringToTTL(l.token)
+                               if !ok {
+                                       return zp.setParseError("not a TTL", l)
+                               }
+
+                               h.Ttl = ttl
+
+                               if zp.defttl == nil || !zp.defttl.isByDirective {
+                                       zp.defttl = &ttlState{ttl, false}
+                               }
+
+                               st = zExpectAnyNoTTLBl
+                       default:
+                               return zp.setParseError("syntax error at beginning", l)
+                       }
+               case zExpectDirIncludeBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank after $INCLUDE-directive", l)
+                       }
+
+                       st = zExpectDirInclude
+               case zExpectDirInclude:
+                       if l.value != zString {
+                               return zp.setParseError("expecting $INCLUDE value, not this...", l)
+                       }
+
+                       neworigin := zp.origin // There may be optionally a new origin set after the filename, if not use current one
+                       switch l, _ := zp.c.Next(); l.value {
+                       case zBlank:
+                               l, _ := zp.c.Next()
+                               if l.value == zString {
+                                       name, ok := toAbsoluteName(l.token, zp.origin)
+                                       if !ok {
+                                               return zp.setParseError("bad origin name", l)
+                                       }
+
+                                       neworigin = name
+                               }
+                       case zNewline, zEOF:
+                               // Ok
+                       default:
+                               return zp.setParseError("garbage after $INCLUDE", l)
+                       }
+
+                       if !zp.includeAllowed {
+                               return zp.setParseError("$INCLUDE directive not allowed", l)
+                       }
+                       if zp.includeDepth >= maxIncludeDepth {
+                               return zp.setParseError("too deeply nested $INCLUDE", l)
+                       }
+
+                       // Start with the new file
+                       includePath := l.token
+                       if !filepath.IsAbs(includePath) {
+                               includePath = filepath.Join(filepath.Dir(zp.file), includePath)
+                       }
+
+                       r1, e1 := os.Open(includePath)
+                       if e1 != nil {
+                               var as string
+                               if !filepath.IsAbs(l.token) {
+                                       as = fmt.Sprintf(" as `%s'", includePath)
+                               }
+
+                               msg := fmt.Sprintf("failed to open `%s'%s: %v", l.token, as, e1)
+                               return zp.setParseError(msg, l)
+                       }
+
+                       zp.sub = NewZoneParser(r1, neworigin, includePath)
+                       zp.sub.defttl, zp.sub.includeDepth, zp.sub.osFile = zp.defttl, zp.includeDepth+1, r1
+                       zp.sub.SetIncludeAllowed(true)
+                       return zp.subNext()
+               case zExpectDirTTLBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank after $TTL-directive", l)
+                       }
+
+                       st = zExpectDirTTL
+               case zExpectDirTTL:
+                       if l.value != zString {
+                               return zp.setParseError("expecting $TTL value, not this...", l)
+                       }
+
+                       if e := slurpRemainder(zp.c, zp.file); e != nil {
+                               zp.parseErr = e
+                               return nil, false
+                       }
+
+                       ttl, ok := stringToTTL(l.token)
+                       if !ok {
+                               return zp.setParseError("expecting $TTL value, not this...", l)
+                       }
+
+                       zp.defttl = &ttlState{ttl, true}
+
+                       st = zExpectOwnerDir
+               case zExpectDirOriginBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank after $ORIGIN-directive", l)
+                       }
+
+                       st = zExpectDirOrigin
+               case zExpectDirOrigin:
+                       if l.value != zString {
+                               return zp.setParseError("expecting $ORIGIN value, not this...", l)
+                       }
+
+                       if e := slurpRemainder(zp.c, zp.file); e != nil {
+                               zp.parseErr = e
+                               return nil, false
+                       }
+
+                       name, ok := toAbsoluteName(l.token, zp.origin)
+                       if !ok {
+                               return zp.setParseError("bad origin name", l)
+                       }
+
+                       zp.origin = name
+
+                       st = zExpectOwnerDir
+               case zExpectDirGenerateBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank after $GENERATE-directive", l)
+                       }
+
+                       st = zExpectDirGenerate
+               case zExpectDirGenerate:
+                       if l.value != zString {
+                               return zp.setParseError("expecting $GENERATE value, not this...", l)
+                       }
+
+                       return zp.generate(l)
+               case zExpectOwnerBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank after owner", l)
+                       }
+
+                       st = zExpectAny
+               case zExpectAny:
+                       switch l.value {
+                       case zRrtpe:
+                               if zp.defttl == nil {
+                                       return zp.setParseError("missing TTL with no previous value", l)
+                               }
+
+                               h.Rrtype = l.torc
+
+                               st = zExpectRdata
+                       case zClass:
+                               h.Class = l.torc
+
+                               st = zExpectAnyNoClassBl
+                       case zString:
+                               ttl, ok := stringToTTL(l.token)
+                               if !ok {
+                                       return zp.setParseError("not a TTL", l)
+                               }
+
+                               h.Ttl = ttl
+
+                               if zp.defttl == nil || !zp.defttl.isByDirective {
+                                       zp.defttl = &ttlState{ttl, false}
+                               }
+
+                               st = zExpectAnyNoTTLBl
+                       default:
+                               return zp.setParseError("expecting RR type, TTL or class, not this...", l)
+                       }
+               case zExpectAnyNoClassBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank before class", l)
+                       }
+
+                       st = zExpectAnyNoClass
+               case zExpectAnyNoTTLBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank before TTL", l)
+                       }
+
+                       st = zExpectAnyNoTTL
+               case zExpectAnyNoTTL:
+                       switch l.value {
+                       case zClass:
+                               h.Class = l.torc
+
+                               st = zExpectRrtypeBl
+                       case zRrtpe:
+                               h.Rrtype = l.torc
+
+                               st = zExpectRdata
+                       default:
+                               return zp.setParseError("expecting RR type or class, not this...", l)
+                       }
+               case zExpectAnyNoClass:
+                       switch l.value {
+                       case zString:
+                               ttl, ok := stringToTTL(l.token)
+                               if !ok {
+                                       return zp.setParseError("not a TTL", l)
+                               }
+
+                               h.Ttl = ttl
+
+                               if zp.defttl == nil || !zp.defttl.isByDirective {
+                                       zp.defttl = &ttlState{ttl, false}
+                               }
+
+                               st = zExpectRrtypeBl
+                       case zRrtpe:
+                               h.Rrtype = l.torc
+
+                               st = zExpectRdata
+                       default:
+                               return zp.setParseError("expecting RR type or TTL, not this...", l)
+                       }
+               case zExpectRrtypeBl:
+                       if l.value != zBlank {
+                               return zp.setParseError("no blank before RR type", l)
+                       }
+
+                       st = zExpectRrtype
+               case zExpectRrtype:
+                       if l.value != zRrtpe {
+                               return zp.setParseError("unknown RR type", l)
+                       }
+
+                       h.Rrtype = l.torc
+
+                       st = zExpectRdata
+               case zExpectRdata:
+                       r, e := setRR(*h, zp.c, zp.origin, zp.file)
+                       if e != nil {
+                               // If e.lex is nil than we have encounter a unknown RR type
+                               // in that case we substitute our current lex token
+                               if e.lex.token == "" && e.lex.value == 0 {
+                                       e.lex = l // Uh, dirty
+                               }
+
+                               zp.parseErr = e
+                               return nil, false
+                       }
+
+                       return r, true
+               }
+       }
+
+       // If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
+       // is not an error, because an empty zone file is still a zone file.
+       return nil, false
+}
+
+type zlexer struct {
+       br io.ByteReader
+
+       readErr error
+
+       line   int
+       column int
+
+       comBuf  string
+       comment string
+
+       l lex
+
+       brace  int
+       quote  bool
+       space  bool
+       commt  bool
+       rrtype bool
+       owner  bool
+
+       nextL bool
+
+       eol bool // end-of-line
+}
+
+func newZLexer(r io.Reader) *zlexer {
+       br, ok := r.(io.ByteReader)
+       if !ok {
+               br = bufio.NewReaderSize(r, 1024)
+       }
+
+       return &zlexer{
+               br: br,
+
+               line: 1,
+
+               owner: true,
+       }
+}
+
+func (zl *zlexer) Err() error {
+       if zl.readErr == io.EOF {
+               return nil
+       }
+
+       return zl.readErr
+}
+
+// readByte returns the next byte from the input
+func (zl *zlexer) readByte() (byte, bool) {
+       if zl.readErr != nil {
+               return 0, false
+       }
+
+       c, err := zl.br.ReadByte()
+       if err != nil {
+               zl.readErr = err
+               return 0, false
+       }
+
+       // delay the newline handling until the next token is delivered,
+       // fixes off-by-one errors when reporting a parse error.
+       if zl.eol {
+               zl.line++
+               zl.column = 0
+               zl.eol = false
+       }
+
+       if c == '\n' {
+               zl.eol = true
+       } else {
+               zl.column++
+       }
+
+       return c, true
+}
+
+func (zl *zlexer) Next() (lex, bool) {
+       l := &zl.l
+       if zl.nextL {
+               zl.nextL = false
+               return *l, true
+       }
+       if l.err {
+               // Parsing errors should be sticky.
+               return lex{value: zEOF}, false
+       }
+
+       var (
+               str [maxTok]byte // Hold string text
+               com [maxTok]byte // Hold comment text
+
+               stri int // Offset in str (0 means empty)
+               comi int // Offset in com (0 means empty)
+
+               escape bool
+       )
+
+       if zl.comBuf != "" {
+               comi = copy(com[:], zl.comBuf)
+               zl.comBuf = ""
+       }
+
+       zl.comment = ""
+
+       for x, ok := zl.readByte(); ok; x, ok = zl.readByte() {
+               l.line, l.column = zl.line, zl.column
+
+               if stri >= len(str) {
+                       l.token = "token length insufficient for parsing"
+                       l.err = true
+                       return *l, true
+               }
+               if comi >= len(com) {
+                       l.token = "comment length insufficient for parsing"
+                       l.err = true
+                       return *l, true
+               }
+
+               switch x {
+               case ' ', '\t':
+                       if escape || zl.quote {
+                               // Inside quotes or escaped this is legal.
+                               str[stri] = x
+                               stri++
+
+                               escape = false
+                               break
+                       }
+
+                       if zl.commt {
+                               com[comi] = x
+                               comi++
+                               break
+                       }
+
+                       var retL lex
+                       if stri == 0 {
+                               // Space directly in the beginning, handled in the grammar
+                       } else if zl.owner {
+                               // If we have a string and its the first, make it an owner
+                               l.value = zOwner
+                               l.token = string(str[:stri])
+
+                               // escape $... start with a \ not a $, so this will work
+                               switch strings.ToUpper(l.token) {
+                               case "$TTL":
+                                       l.value = zDirTTL
+                               case "$ORIGIN":
+                                       l.value = zDirOrigin
+                               case "$INCLUDE":
+                                       l.value = zDirInclude
+                               case "$GENERATE":
+                                       l.value = zDirGenerate
+                               }
+
+                               retL = *l
+                       } else {
+                               l.value = zString
+                               l.token = string(str[:stri])
+
+                               if !zl.rrtype {
+                                       tokenUpper := strings.ToUpper(l.token)
+                                       if t, ok := StringToType[tokenUpper]; ok {
+                                               l.value = zRrtpe
+                                               l.torc = t
+
+                                               zl.rrtype = true
+                                       } else if strings.HasPrefix(tokenUpper, "TYPE") {
+                                               t, ok := typeToInt(l.token)
+                                               if !ok {
+                                                       l.token = "unknown RR type"
+                                                       l.err = true
+                                                       return *l, true
+                                               }
+
+                                               l.value = zRrtpe
+                                               l.torc = t
+
+                                               zl.rrtype = true
+                                       }
+
+                                       if t, ok := StringToClass[tokenUpper]; ok {
+                                               l.value = zClass
+                                               l.torc = t
+                                       } else if strings.HasPrefix(tokenUpper, "CLASS") {
+                                               t, ok := classToInt(l.token)
+                                               if !ok {
+                                                       l.token = "unknown class"
+                                                       l.err = true
+                                                       return *l, true
+                                               }
+
+                                               l.value = zClass
+                                               l.torc = t
+                                       }
+                               }
+
+                               retL = *l
+                       }
+
+                       zl.owner = false
+
+                       if !zl.space {
+                               zl.space = true
+
+                               l.value = zBlank
+                               l.token = " "
+
+                               if retL == (lex{}) {
+                                       return *l, true
+                               }
+
+                               zl.nextL = true
+                       }
+
+                       if retL != (lex{}) {
+                               return retL, true
+                       }
+               case ';':
+                       if escape || zl.quote {
+                               // Inside quotes or escaped this is legal.
+                               str[stri] = x
+                               stri++
+
+                               escape = false
+                               break
+                       }
+
+                       zl.commt = true
+                       zl.comBuf = ""
+
+                       if comi > 1 {
+                               // A newline was previously seen inside a comment that
+                               // was inside braces and we delayed adding it until now.
+                               com[comi] = ' ' // convert newline to space
+                               comi++
+                       }
+
+                       com[comi] = ';'
+                       comi++
+
+                       if stri > 0 {
+                               zl.comBuf = string(com[:comi])
+
+                               l.value = zString
+                               l.token = string(str[:stri])
+                               return *l, true
+                       }
+               case '\r':
+                       escape = false
+
+                       if zl.quote {
+                               str[stri] = x
+                               stri++
+                       }
+
+                       // discard if outside of quotes
+               case '\n':
+                       escape = false
+
+                       // Escaped newline
+                       if zl.quote {
+                               str[stri] = x
+                               stri++
+                               break
+                       }
+
+                       if zl.commt {
+                               // Reset a comment
+                               zl.commt = false
+                               zl.rrtype = false
+
+                               // If not in a brace this ends the comment AND the RR
+                               if zl.brace == 0 {
+                                       zl.owner = true
+
+                                       l.value = zNewline
+                                       l.token = "\n"
+                                       zl.comment = string(com[:comi])
+                                       return *l, true
+                               }
+
+                               zl.comBuf = string(com[:comi])
+                               break
+                       }
+
+                       if zl.brace == 0 {
+                               // If there is previous text, we should output it here
+                               var retL lex
+                               if stri != 0 {
+                                       l.value = zString
+                                       l.token = string(str[:stri])
+
+                                       if !zl.rrtype {
+                                               tokenUpper := strings.ToUpper(l.token)
+                                               if t, ok := StringToType[tokenUpper]; ok {
+                                                       zl.rrtype = true
+
+                                                       l.value = zRrtpe
+                                                       l.torc = t
+                                               }
+                                       }
+
+                                       retL = *l
+                               }
+
+                               l.value = zNewline
+                               l.token = "\n"
+
+                               zl.comment = zl.comBuf
+                               zl.comBuf = ""
+                               zl.rrtype = false
+                               zl.owner = true
+
+                               if retL != (lex{}) {
+                                       zl.nextL = true
+                                       return retL, true
+                               }
+
+                               return *l, true
+                       }
+               case '\\':
+                       // comments do not get escaped chars, everything is copied
+                       if zl.commt {
+                               com[comi] = x
+                               comi++
+                               break
+                       }
+
+                       // something already escaped must be in string
+                       if escape {
+                               str[stri] = x
+                               stri++
+
+                               escape = false
+                               break
+                       }
+
+                       // something escaped outside of string gets added to string
+                       str[stri] = x
+                       stri++
+
+                       escape = true
+               case '"':
+                       if zl.commt {
+                               com[comi] = x
+                               comi++
+                               break
+                       }
+
+                       if escape {
+                               str[stri] = x
+                               stri++
+
+                               escape = false
+                               break
+                       }
+
+                       zl.space = false
+
+                       // send previous gathered text and the quote
+                       var retL lex
+                       if stri != 0 {
+                               l.value = zString
+                               l.token = string(str[:stri])
+
+                               retL = *l
+                       }
+
+                       // send quote itself as separate token
+                       l.value = zQuote
+                       l.token = "\""
+
+                       zl.quote = !zl.quote
+
+                       if retL != (lex{}) {
+                               zl.nextL = true
+                               return retL, true
+                       }
+
+                       return *l, true
+               case '(', ')':
+                       if zl.commt {
+                               com[comi] = x
+                               comi++
+                               break
+                       }
+
+                       if escape || zl.quote {
+                               // Inside quotes or escaped this is legal.
+                               str[stri] = x
+                               stri++
+
+                               escape = false
+                               break
+                       }
+
+                       switch x {
+                       case ')':
+                               zl.brace--
+
+                               if zl.brace < 0 {
+                                       l.token = "extra closing brace"
+                                       l.err = true
+                                       return *l, true
+                               }
+                       case '(':
+                               zl.brace++
+                       }
+               default:
+                       escape = false
+
+                       if zl.commt {
+                               com[comi] = x
+                               comi++
+                               break
+                       }
+
+                       str[stri] = x
+                       stri++
+
+                       zl.space = false
+               }
+       }
+
+       if zl.readErr != nil && zl.readErr != io.EOF {
+               // Don't return any tokens after a read error occurs.
+               return lex{value: zEOF}, false
+       }
+
+       var retL lex
+       if stri > 0 {
+               // Send remainder of str
+               l.value = zString
+               l.token = string(str[:stri])
+               retL = *l
+
+               if comi <= 0 {
+                       return retL, true
+               }
+       }
+
+       if comi > 0 {
+               // Send remainder of com
+               l.value = zNewline
+               l.token = "\n"
+               zl.comment = string(com[:comi])
+
+               if retL != (lex{}) {
+                       zl.nextL = true
+                       return retL, true
+               }
+
+               return *l, true
+       }
+
+       if zl.brace != 0 {
+               l.token = "unbalanced brace"
+               l.err = true
+               return *l, true
+       }
+
+       return lex{value: zEOF}, false
+}
+
+func (zl *zlexer) Comment() string {
+       if zl.l.err {
+               return ""
+       }
+
+       return zl.comment
+}
+
+// Extract the class number from CLASSxx
+func classToInt(token string) (uint16, bool) {
+       offset := 5
+       if len(token) < offset+1 {
+               return 0, false
+       }
+       class, err := strconv.ParseUint(token[offset:], 10, 16)
+       if err != nil {
+               return 0, false
+       }
+       return uint16(class), true
+}
+
+// Extract the rr number from TYPExxx
+func typeToInt(token string) (uint16, bool) {
+       offset := 4
+       if len(token) < offset+1 {
+               return 0, false
+       }
+       typ, err := strconv.ParseUint(token[offset:], 10, 16)
+       if err != nil {
+               return 0, false
+       }
+       return uint16(typ), true
+}
+
+// stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds.
+func stringToTTL(token string) (uint32, bool) {
+       var s, i uint32
+       for _, c := range token {
+               switch c {
+               case 's', 'S':
+                       s += i
+                       i = 0
+               case 'm', 'M':
+                       s += i * 60
+                       i = 0
+               case 'h', 'H':
+                       s += i * 60 * 60
+                       i = 0
+               case 'd', 'D':
+                       s += i * 60 * 60 * 24
+                       i = 0
+               case 'w', 'W':
+                       s += i * 60 * 60 * 24 * 7
+                       i = 0
+               case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+                       i *= 10
+                       i += uint32(c) - '0'
+               default:
+                       return 0, false
+               }
+       }
+       return s + i, true
+}
+
+// Parse LOC records' <digits>[.<digits>][mM] into a
+// mantissa exponent format. Token should contain the entire
+// string (i.e. no spaces allowed)
+func stringToCm(token string) (e, m uint8, ok bool) {
+       if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {
+               token = token[0 : len(token)-1]
+       }
+       s := strings.SplitN(token, ".", 2)
+       var meters, cmeters, val int
+       var err error
+       switch len(s) {
+       case 2:
+               if cmeters, err = strconv.Atoi(s[1]); err != nil {
+                       return
+               }
+               fallthrough
+       case 1:
+               if meters, err = strconv.Atoi(s[0]); err != nil {
+                       return
+               }
+       case 0:
+               // huh?
+               return 0, 0, false
+       }
+       ok = true
+       if meters > 0 {
+               e = 2
+               val = meters
+       } else {
+               e = 0
+               val = cmeters
+       }
+       for val > 10 {
+               e++
+               val /= 10
+       }
+       if e > 9 {
+               ok = false
+       }
+       m = uint8(val)
+       return
+}
+
+func toAbsoluteName(name, origin string) (absolute string, ok bool) {
+       // check for an explicit origin reference
+       if name == "@" {
+               // require a nonempty origin
+               if origin == "" {
+                       return "", false
+               }
+               return origin, true
+       }
+
+       // require a valid domain name
+       _, ok = IsDomainName(name)
+       if !ok || name == "" {
+               return "", false
+       }
+
+       // check if name is already absolute
+       if IsFqdn(name) {
+               return name, true
+       }
+
+       // require a nonempty origin
+       if origin == "" {
+               return "", false
+       }
+       return appendOrigin(name, origin), true
+}
+
+func appendOrigin(name, origin string) string {
+       if origin == "." {
+               return name + origin
+       }
+       return name + "." + origin
+}
+
+// LOC record helper function
+func locCheckNorth(token string, latitude uint32) (uint32, bool) {
+       switch token {
+       case "n", "N":
+               return LOC_EQUATOR + latitude, true
+       case "s", "S":
+               return LOC_EQUATOR - latitude, true
+       }
+       return latitude, false
+}
+
+// LOC record helper function
+func locCheckEast(token string, longitude uint32) (uint32, bool) {
+       switch token {
+       case "e", "E":
+               return LOC_EQUATOR + longitude, true
+       case "w", "W":
+               return LOC_EQUATOR - longitude, true
+       }
+       return longitude, false
+}
+
+// "Eat" the rest of the "line"
+func slurpRemainder(c *zlexer, f string) *ParseError {
+       l, _ := c.Next()
+       switch l.value {
+       case zBlank:
+               l, _ = c.Next()
+               if l.value != zNewline && l.value != zEOF {
+                       return &ParseError{f, "garbage after rdata", l}
+               }
+       case zNewline:
+       case zEOF:
+       default:
+               return &ParseError{f, "garbage after rdata", l}
+       }
+       return nil
+}
+
+// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64"
+// Used for NID and L64 record.
+func stringToNodeID(l lex) (uint64, *ParseError) {
+       if len(l.token) < 19 {
+               return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
+       }
+       // There must be three colons at fixes postitions, if not its a parse error
+       if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
+               return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
+       }
+       s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]
+       u, err := strconv.ParseUint(s, 16, 64)
+       if err != nil {
+               return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
+       }
+       return u, nil
+}
diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go
new file mode 100644 (file)
index 0000000..f48ff78
--- /dev/null
@@ -0,0 +1,1937 @@
+package dns
+
+import (
+       "encoding/base64"
+       "net"
+       "strconv"
+       "strings"
+)
+
+// Parse the rdata of each rrtype.
+// All data from the channel c is either zString or zBlank.
+// After the rdata there may come a zBlank and then a zNewline
+// or immediately a zNewline. If this is not the case we flag
+// an *ParseError: garbage after rdata.
+func setRR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError) {
+       var rr RR
+       if newFn, ok := TypeToRR[h.Rrtype]; ok && canParseAsRR(h.Rrtype) {
+               rr = newFn()
+               *rr.Header() = h
+       } else {
+               rr = &RFC3597{Hdr: h}
+       }
+
+       err := rr.parse(c, o, f)
+       if err != nil {
+               return nil, err
+       }
+
+       return rr, nil
+}
+
+// canParseAsRR returns true if the record type can be parsed as a
+// concrete RR. It blacklists certain record types that must be parsed
+// according to RFC 3597 because they lack a presentation format.
+func canParseAsRR(rrtype uint16) bool {
+       switch rrtype {
+       case TypeANY, TypeNULL, TypeOPT, TypeTSIG:
+               return false
+       default:
+               return true
+       }
+}
+
+// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces)
+// or an error
+func endingToString(c *zlexer, errstr, f string) (string, *ParseError) {
+       var s string
+       l, _ := c.Next() // zString
+       for l.value != zNewline && l.value != zEOF {
+               if l.err {
+                       return s, &ParseError{f, errstr, l}
+               }
+               switch l.value {
+               case zString:
+                       s += l.token
+               case zBlank: // Ok
+               default:
+                       return "", &ParseError{f, errstr, l}
+               }
+               l, _ = c.Next()
+       }
+
+       return s, nil
+}
+
+// A remainder of the rdata with embedded spaces, split on unquoted whitespace
+// and return the parsed string slice or an error
+func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError) {
+       // Get the remaining data until we see a zNewline
+       l, _ := c.Next()
+       if l.err {
+               return nil, &ParseError{f, errstr, l}
+       }
+
+       // Build the slice
+       s := make([]string, 0)
+       quote := false
+       empty := false
+       for l.value != zNewline && l.value != zEOF {
+               if l.err {
+                       return nil, &ParseError{f, errstr, l}
+               }
+               switch l.value {
+               case zString:
+                       empty = false
+                       if len(l.token) > 255 {
+                               // split up tokens that are larger than 255 into 255-chunks
+                               sx := []string{}
+                               p, i := 0, 255
+                               for {
+                                       if i <= len(l.token) {
+                                               sx = append(sx, l.token[p:i])
+                                       } else {
+                                               sx = append(sx, l.token[p:])
+                                               break
+
+                                       }
+                                       p, i = p+255, i+255
+                               }
+                               s = append(s, sx...)
+                               break
+                       }
+
+                       s = append(s, l.token)
+               case zBlank:
+                       if quote {
+                               // zBlank can only be seen in between txt parts.
+                               return nil, &ParseError{f, errstr, l}
+                       }
+               case zQuote:
+                       if empty && quote {
+                               s = append(s, "")
+                       }
+                       quote = !quote
+                       empty = true
+               default:
+                       return nil, &ParseError{f, errstr, l}
+               }
+               l, _ = c.Next()
+       }
+
+       if quote {
+               return nil, &ParseError{f, errstr, l}
+       }
+
+       return s, nil
+}
+
+func (rr *A) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       rr.A = net.ParseIP(l.token)
+       if rr.A == nil || l.err {
+               return &ParseError{f, "bad A A", l}
+       }
+       return slurpRemainder(c, f)
+}
+
+func (rr *AAAA) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       rr.AAAA = net.ParseIP(l.token)
+       if rr.AAAA == nil || l.err {
+               return &ParseError{f, "bad AAAA AAAA", l}
+       }
+       return slurpRemainder(c, f)
+}
+
+func (rr *NS) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Ns = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad NS Ns", l}
+       }
+       rr.Ns = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *PTR) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Ptr = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad PTR Ptr", l}
+       }
+       rr.Ptr = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *NSAPPTR) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Ptr = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad NSAP-PTR Ptr", l}
+       }
+       rr.Ptr = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *RP) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Mbox = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       mbox, mboxOk := toAbsoluteName(l.token, o)
+       if l.err || !mboxOk {
+               return &ParseError{f, "bad RP Mbox", l}
+       }
+       rr.Mbox = mbox
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       rr.Txt = l.token
+
+       txt, txtOk := toAbsoluteName(l.token, o)
+       if l.err || !txtOk {
+               return &ParseError{f, "bad RP Txt", l}
+       }
+       rr.Txt = txt
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *MR) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Mr = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad MR Mr", l}
+       }
+       rr.Mr = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *MB) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Mb = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad MB Mb", l}
+       }
+       rr.Mb = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *MG) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Mg = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad MG Mg", l}
+       }
+       rr.Mg = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *HINFO) parse(c *zlexer, o, f string) *ParseError {
+       chunks, e := endingToTxtSlice(c, "bad HINFO Fields", f)
+       if e != nil {
+               return e
+       }
+
+       if ln := len(chunks); ln == 0 {
+               return nil
+       } else if ln == 1 {
+               // Can we split it?
+               if out := strings.Fields(chunks[0]); len(out) > 1 {
+                       chunks = out
+               } else {
+                       chunks = append(chunks, "")
+               }
+       }
+
+       rr.Cpu = chunks[0]
+       rr.Os = strings.Join(chunks[1:], " ")
+
+       return nil
+}
+
+func (rr *MINFO) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Rmail = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       rmail, rmailOk := toAbsoluteName(l.token, o)
+       if l.err || !rmailOk {
+               return &ParseError{f, "bad MINFO Rmail", l}
+       }
+       rr.Rmail = rmail
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       rr.Email = l.token
+
+       email, emailOk := toAbsoluteName(l.token, o)
+       if l.err || !emailOk {
+               return &ParseError{f, "bad MINFO Email", l}
+       }
+       rr.Email = email
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *MF) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Mf = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad MF Mf", l}
+       }
+       rr.Mf = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *MD) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Md = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad MD Md", l}
+       }
+       rr.Md = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *MX) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad MX Pref", l}
+       }
+       rr.Preference = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Mx = l.token
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad MX Mx", l}
+       }
+       rr.Mx = name
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *RT) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil {
+               return &ParseError{f, "bad RT Preference", l}
+       }
+       rr.Preference = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Host = l.token
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad RT Host", l}
+       }
+       rr.Host = name
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *AFSDB) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad AFSDB Subtype", l}
+       }
+       rr.Subtype = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Hostname = l.token
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad AFSDB Hostname", l}
+       }
+       rr.Hostname = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *X25) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       if l.err {
+               return &ParseError{f, "bad X25 PSDNAddress", l}
+       }
+       rr.PSDNAddress = l.token
+       return slurpRemainder(c, f)
+}
+
+func (rr *KX) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad KX Pref", l}
+       }
+       rr.Preference = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Exchanger = l.token
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad KX Exchanger", l}
+       }
+       rr.Exchanger = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *CNAME) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Target = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad CNAME Target", l}
+       }
+       rr.Target = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *DNAME) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Target = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad DNAME Target", l}
+       }
+       rr.Target = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *SOA) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.Ns = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       ns, nsOk := toAbsoluteName(l.token, o)
+       if l.err || !nsOk {
+               return &ParseError{f, "bad SOA Ns", l}
+       }
+       rr.Ns = ns
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       rr.Mbox = l.token
+
+       mbox, mboxOk := toAbsoluteName(l.token, o)
+       if l.err || !mboxOk {
+               return &ParseError{f, "bad SOA Mbox", l}
+       }
+       rr.Mbox = mbox
+
+       c.Next() // zBlank
+
+       var (
+               v  uint32
+               ok bool
+       )
+       for i := 0; i < 5; i++ {
+               l, _ = c.Next()
+               if l.err {
+                       return &ParseError{f, "bad SOA zone parameter", l}
+               }
+               if j, e := strconv.ParseUint(l.token, 10, 32); e != nil {
+                       if i == 0 {
+                               // Serial must be a number
+                               return &ParseError{f, "bad SOA zone parameter", l}
+                       }
+                       // We allow other fields to be unitful duration strings
+                       if v, ok = stringToTTL(l.token); !ok {
+                               return &ParseError{f, "bad SOA zone parameter", l}
+
+                       }
+               } else {
+                       v = uint32(j)
+               }
+               switch i {
+               case 0:
+                       rr.Serial = v
+                       c.Next() // zBlank
+               case 1:
+                       rr.Refresh = v
+                       c.Next() // zBlank
+               case 2:
+                       rr.Retry = v
+                       c.Next() // zBlank
+               case 3:
+                       rr.Expire = v
+                       c.Next() // zBlank
+               case 4:
+                       rr.Minttl = v
+               }
+       }
+       return slurpRemainder(c, f)
+}
+
+func (rr *SRV) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SRV Priority", l}
+       }
+       rr.Priority = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SRV Weight", l}
+       }
+       rr.Weight = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SRV Port", l}
+       }
+       rr.Port = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Target = l.token
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad SRV Target", l}
+       }
+       rr.Target = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *NAPTR) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NAPTR Order", l}
+       }
+       rr.Order = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NAPTR Preference", l}
+       }
+       rr.Preference = uint16(i)
+
+       // Flags
+       c.Next()        // zBlank
+       l, _ = c.Next() // _QUOTE
+       if l.value != zQuote {
+               return &ParseError{f, "bad NAPTR Flags", l}
+       }
+       l, _ = c.Next() // Either String or Quote
+       if l.value == zString {
+               rr.Flags = l.token
+               l, _ = c.Next() // _QUOTE
+               if l.value != zQuote {
+                       return &ParseError{f, "bad NAPTR Flags", l}
+               }
+       } else if l.value == zQuote {
+               rr.Flags = ""
+       } else {
+               return &ParseError{f, "bad NAPTR Flags", l}
+       }
+
+       // Service
+       c.Next()        // zBlank
+       l, _ = c.Next() // _QUOTE
+       if l.value != zQuote {
+               return &ParseError{f, "bad NAPTR Service", l}
+       }
+       l, _ = c.Next() // Either String or Quote
+       if l.value == zString {
+               rr.Service = l.token
+               l, _ = c.Next() // _QUOTE
+               if l.value != zQuote {
+                       return &ParseError{f, "bad NAPTR Service", l}
+               }
+       } else if l.value == zQuote {
+               rr.Service = ""
+       } else {
+               return &ParseError{f, "bad NAPTR Service", l}
+       }
+
+       // Regexp
+       c.Next()        // zBlank
+       l, _ = c.Next() // _QUOTE
+       if l.value != zQuote {
+               return &ParseError{f, "bad NAPTR Regexp", l}
+       }
+       l, _ = c.Next() // Either String or Quote
+       if l.value == zString {
+               rr.Regexp = l.token
+               l, _ = c.Next() // _QUOTE
+               if l.value != zQuote {
+                       return &ParseError{f, "bad NAPTR Regexp", l}
+               }
+       } else if l.value == zQuote {
+               rr.Regexp = ""
+       } else {
+               return &ParseError{f, "bad NAPTR Regexp", l}
+       }
+
+       // After quote no space??
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Replacement = l.token
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad NAPTR Replacement", l}
+       }
+       rr.Replacement = name
+       return slurpRemainder(c, f)
+}
+
+func (rr *TALINK) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.PreviousName = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       previousName, previousNameOk := toAbsoluteName(l.token, o)
+       if l.err || !previousNameOk {
+               return &ParseError{f, "bad TALINK PreviousName", l}
+       }
+       rr.PreviousName = previousName
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       rr.NextName = l.token
+
+       nextName, nextNameOk := toAbsoluteName(l.token, o)
+       if l.err || !nextNameOk {
+               return &ParseError{f, "bad TALINK NextName", l}
+       }
+       rr.NextName = nextName
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *LOC) parse(c *zlexer, o, f string) *ParseError {
+       // Non zero defaults for LOC record, see RFC 1876, Section 3.
+       rr.HorizPre = 165 // 10000
+       rr.VertPre = 162  // 10
+       rr.Size = 18      // 1
+       ok := false
+
+       // North
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+       i, e := strconv.ParseUint(l.token, 10, 32)
+       if e != nil || l.err {
+               return &ParseError{f, "bad LOC Latitude", l}
+       }
+       rr.Latitude = 1000 * 60 * 60 * uint32(i)
+
+       c.Next() // zBlank
+       // Either number, 'N' or 'S'
+       l, _ = c.Next()
+       if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
+               goto East
+       }
+       i, e = strconv.ParseUint(l.token, 10, 32)
+       if e != nil || l.err {
+               return &ParseError{f, "bad LOC Latitude minutes", l}
+       }
+       rr.Latitude += 1000 * 60 * uint32(i)
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
+               return &ParseError{f, "bad LOC Latitude seconds", l}
+       } else {
+               rr.Latitude += uint32(1000 * i)
+       }
+       c.Next() // zBlank
+       // Either number, 'N' or 'S'
+       l, _ = c.Next()
+       if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
+               goto East
+       }
+       // If still alive, flag an error
+       return &ParseError{f, "bad LOC Latitude North/South", l}
+
+East:
+       // East
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err {
+               return &ParseError{f, "bad LOC Longitude", l}
+       } else {
+               rr.Longitude = 1000 * 60 * 60 * uint32(i)
+       }
+       c.Next() // zBlank
+       // Either number, 'E' or 'W'
+       l, _ = c.Next()
+       if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
+               goto Altitude
+       }
+       if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err {
+               return &ParseError{f, "bad LOC Longitude minutes", l}
+       } else {
+               rr.Longitude += 1000 * 60 * uint32(i)
+       }
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
+               return &ParseError{f, "bad LOC Longitude seconds", l}
+       } else {
+               rr.Longitude += uint32(1000 * i)
+       }
+       c.Next() // zBlank
+       // Either number, 'E' or 'W'
+       l, _ = c.Next()
+       if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
+               goto Altitude
+       }
+       // If still alive, flag an error
+       return &ParseError{f, "bad LOC Longitude East/West", l}
+
+Altitude:
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if len(l.token) == 0 || l.err {
+               return &ParseError{f, "bad LOC Altitude", l}
+       }
+       if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
+               l.token = l.token[0 : len(l.token)-1]
+       }
+       if i, e := strconv.ParseFloat(l.token, 32); e != nil {
+               return &ParseError{f, "bad LOC Altitude", l}
+       } else {
+               rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5)
+       }
+
+       // And now optionally the other values
+       l, _ = c.Next()
+       count := 0
+       for l.value != zNewline && l.value != zEOF {
+               switch l.value {
+               case zString:
+                       switch count {
+                       case 0: // Size
+                               e, m, ok := stringToCm(l.token)
+                               if !ok {
+                                       return &ParseError{f, "bad LOC Size", l}
+                               }
+                               rr.Size = e&0x0f | m<<4&0xf0
+                       case 1: // HorizPre
+                               e, m, ok := stringToCm(l.token)
+                               if !ok {
+                                       return &ParseError{f, "bad LOC HorizPre", l}
+                               }
+                               rr.HorizPre = e&0x0f | m<<4&0xf0
+                       case 2: // VertPre
+                               e, m, ok := stringToCm(l.token)
+                               if !ok {
+                                       return &ParseError{f, "bad LOC VertPre", l}
+                               }
+                               rr.VertPre = e&0x0f | m<<4&0xf0
+                       }
+                       count++
+               case zBlank:
+                       // Ok
+               default:
+                       return &ParseError{f, "bad LOC Size, HorizPre or VertPre", l}
+               }
+               l, _ = c.Next()
+       }
+       return nil
+}
+
+func (rr *HIP) parse(c *zlexer, o, f string) *ParseError {
+       // HitLength is not represented
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad HIP PublicKeyAlgorithm", l}
+       }
+       rr.PublicKeyAlgorithm = uint8(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       if len(l.token) == 0 || l.err {
+               return &ParseError{f, "bad HIP Hit", l}
+       }
+       rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
+       rr.HitLength = uint8(len(rr.Hit)) / 2
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       if len(l.token) == 0 || l.err {
+               return &ParseError{f, "bad HIP PublicKey", l}
+       }
+       rr.PublicKey = l.token // This cannot contain spaces
+       rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey)))
+
+       // RendezvousServers (if any)
+       l, _ = c.Next()
+       var xs []string
+       for l.value != zNewline && l.value != zEOF {
+               switch l.value {
+               case zString:
+                       name, nameOk := toAbsoluteName(l.token, o)
+                       if l.err || !nameOk {
+                               return &ParseError{f, "bad HIP RendezvousServers", l}
+                       }
+                       xs = append(xs, name)
+               case zBlank:
+                       // Ok
+               default:
+                       return &ParseError{f, "bad HIP RendezvousServers", l}
+               }
+               l, _ = c.Next()
+       }
+
+       rr.RendezvousServers = xs
+       return nil
+}
+
+func (rr *CERT) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       if v, ok := StringToCertType[l.token]; ok {
+               rr.Type = v
+       } else if i, e := strconv.ParseUint(l.token, 10, 16); e != nil {
+               return &ParseError{f, "bad CERT Type", l}
+       } else {
+               rr.Type = uint16(i)
+       }
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad CERT KeyTag", l}
+       }
+       rr.KeyTag = uint16(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       if v, ok := StringToAlgorithm[l.token]; ok {
+               rr.Algorithm = v
+       } else if i, e := strconv.ParseUint(l.token, 10, 8); e != nil {
+               return &ParseError{f, "bad CERT Algorithm", l}
+       } else {
+               rr.Algorithm = uint8(i)
+       }
+       s, e1 := endingToString(c, "bad CERT Certificate", f)
+       if e1 != nil {
+               return e1
+       }
+       rr.Certificate = s
+       return nil
+}
+
+func (rr *OPENPGPKEY) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToString(c, "bad OPENPGPKEY PublicKey", f)
+       if e != nil {
+               return e
+       }
+       rr.PublicKey = s
+       return nil
+}
+
+func (rr *CSYNC) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+       j, e := strconv.ParseUint(l.token, 10, 32)
+       if e != nil {
+               // Serial must be a number
+               return &ParseError{f, "bad CSYNC serial", l}
+       }
+       rr.Serial = uint32(j)
+
+       c.Next() // zBlank
+
+       l, _ = c.Next()
+       j, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil {
+               // Serial must be a number
+               return &ParseError{f, "bad CSYNC flags", l}
+       }
+       rr.Flags = uint16(j)
+
+       rr.TypeBitMap = make([]uint16, 0)
+       var (
+               k  uint16
+               ok bool
+       )
+       l, _ = c.Next()
+       for l.value != zNewline && l.value != zEOF {
+               switch l.value {
+               case zBlank:
+                       // Ok
+               case zString:
+                       tokenUpper := strings.ToUpper(l.token)
+                       if k, ok = StringToType[tokenUpper]; !ok {
+                               if k, ok = typeToInt(l.token); !ok {
+                                       return &ParseError{f, "bad CSYNC TypeBitMap", l}
+                               }
+                       }
+                       rr.TypeBitMap = append(rr.TypeBitMap, k)
+               default:
+                       return &ParseError{f, "bad CSYNC TypeBitMap", l}
+               }
+               l, _ = c.Next()
+       }
+       return nil
+}
+
+func (rr *SIG) parse(c *zlexer, o, f string) *ParseError {
+       return rr.RRSIG.parse(c, o, f)
+}
+
+func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       tokenUpper := strings.ToUpper(l.token)
+       if t, ok := StringToType[tokenUpper]; !ok {
+               if strings.HasPrefix(tokenUpper, "TYPE") {
+                       t, ok = typeToInt(l.token)
+                       if !ok {
+                               return &ParseError{f, "bad RRSIG Typecovered", l}
+                       }
+                       rr.TypeCovered = t
+               } else {
+                       return &ParseError{f, "bad RRSIG Typecovered", l}
+               }
+       } else {
+               rr.TypeCovered = t
+       }
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, err := strconv.ParseUint(l.token, 10, 8)
+       if err != nil || l.err {
+               return &ParseError{f, "bad RRSIG Algorithm", l}
+       }
+       rr.Algorithm = uint8(i)
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, err = strconv.ParseUint(l.token, 10, 8)
+       if err != nil || l.err {
+               return &ParseError{f, "bad RRSIG Labels", l}
+       }
+       rr.Labels = uint8(i)
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, err = strconv.ParseUint(l.token, 10, 32)
+       if err != nil || l.err {
+               return &ParseError{f, "bad RRSIG OrigTtl", l}
+       }
+       rr.OrigTtl = uint32(i)
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, err := StringToTime(l.token); err != nil {
+               // Try to see if all numeric and use it as epoch
+               if i, err := strconv.ParseInt(l.token, 10, 64); err == nil {
+                       // TODO(miek): error out on > MAX_UINT32, same below
+                       rr.Expiration = uint32(i)
+               } else {
+                       return &ParseError{f, "bad RRSIG Expiration", l}
+               }
+       } else {
+               rr.Expiration = i
+       }
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, err := StringToTime(l.token); err != nil {
+               if i, err := strconv.ParseInt(l.token, 10, 64); err == nil {
+                       rr.Inception = uint32(i)
+               } else {
+                       return &ParseError{f, "bad RRSIG Inception", l}
+               }
+       } else {
+               rr.Inception = i
+       }
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, err = strconv.ParseUint(l.token, 10, 16)
+       if err != nil || l.err {
+               return &ParseError{f, "bad RRSIG KeyTag", l}
+       }
+       rr.KeyTag = uint16(i)
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       rr.SignerName = l.token
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad RRSIG SignerName", l}
+       }
+       rr.SignerName = name
+
+       s, e := endingToString(c, "bad RRSIG Signature", f)
+       if e != nil {
+               return e
+       }
+       rr.Signature = s
+
+       return nil
+}
+
+func (rr *NSEC) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       rr.NextDomain = l.token
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad NSEC NextDomain", l}
+       }
+       rr.NextDomain = name
+
+       rr.TypeBitMap = make([]uint16, 0)
+       var (
+               k  uint16
+               ok bool
+       )
+       l, _ = c.Next()
+       for l.value != zNewline && l.value != zEOF {
+               switch l.value {
+               case zBlank:
+                       // Ok
+               case zString:
+                       tokenUpper := strings.ToUpper(l.token)
+                       if k, ok = StringToType[tokenUpper]; !ok {
+                               if k, ok = typeToInt(l.token); !ok {
+                                       return &ParseError{f, "bad NSEC TypeBitMap", l}
+                               }
+                       }
+                       rr.TypeBitMap = append(rr.TypeBitMap, k)
+               default:
+                       return &ParseError{f, "bad NSEC TypeBitMap", l}
+               }
+               l, _ = c.Next()
+       }
+       return nil
+}
+
+func (rr *NSEC3) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NSEC3 Hash", l}
+       }
+       rr.Hash = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NSEC3 Flags", l}
+       }
+       rr.Flags = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NSEC3 Iterations", l}
+       }
+       rr.Iterations = uint16(i)
+       c.Next()
+       l, _ = c.Next()
+       if len(l.token) == 0 || l.err {
+               return &ParseError{f, "bad NSEC3 Salt", l}
+       }
+       if l.token != "-" {
+               rr.SaltLength = uint8(len(l.token)) / 2
+               rr.Salt = l.token
+       }
+
+       c.Next()
+       l, _ = c.Next()
+       if len(l.token) == 0 || l.err {
+               return &ParseError{f, "bad NSEC3 NextDomain", l}
+       }
+       rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
+       rr.NextDomain = l.token
+
+       rr.TypeBitMap = make([]uint16, 0)
+       var (
+               k  uint16
+               ok bool
+       )
+       l, _ = c.Next()
+       for l.value != zNewline && l.value != zEOF {
+               switch l.value {
+               case zBlank:
+                       // Ok
+               case zString:
+                       tokenUpper := strings.ToUpper(l.token)
+                       if k, ok = StringToType[tokenUpper]; !ok {
+                               if k, ok = typeToInt(l.token); !ok {
+                                       return &ParseError{f, "bad NSEC3 TypeBitMap", l}
+                               }
+                       }
+                       rr.TypeBitMap = append(rr.TypeBitMap, k)
+               default:
+                       return &ParseError{f, "bad NSEC3 TypeBitMap", l}
+               }
+               l, _ = c.Next()
+       }
+       return nil
+}
+
+func (rr *NSEC3PARAM) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NSEC3PARAM Hash", l}
+       }
+       rr.Hash = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NSEC3PARAM Flags", l}
+       }
+       rr.Flags = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NSEC3PARAM Iterations", l}
+       }
+       rr.Iterations = uint16(i)
+       c.Next()
+       l, _ = c.Next()
+       if l.token != "-" {
+               rr.SaltLength = uint8(len(l.token))
+               rr.Salt = l.token
+       }
+       return slurpRemainder(c, f)
+}
+
+func (rr *EUI48) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       if len(l.token) != 17 || l.err {
+               return &ParseError{f, "bad EUI48 Address", l}
+       }
+       addr := make([]byte, 12)
+       dash := 0
+       for i := 0; i < 10; i += 2 {
+               addr[i] = l.token[i+dash]
+               addr[i+1] = l.token[i+1+dash]
+               dash++
+               if l.token[i+1+dash] != '-' {
+                       return &ParseError{f, "bad EUI48 Address", l}
+               }
+       }
+       addr[10] = l.token[15]
+       addr[11] = l.token[16]
+
+       i, e := strconv.ParseUint(string(addr), 16, 48)
+       if e != nil {
+               return &ParseError{f, "bad EUI48 Address", l}
+       }
+       rr.Address = i
+       return slurpRemainder(c, f)
+}
+
+func (rr *EUI64) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       if len(l.token) != 23 || l.err {
+               return &ParseError{f, "bad EUI64 Address", l}
+       }
+       addr := make([]byte, 16)
+       dash := 0
+       for i := 0; i < 14; i += 2 {
+               addr[i] = l.token[i+dash]
+               addr[i+1] = l.token[i+1+dash]
+               dash++
+               if l.token[i+1+dash] != '-' {
+                       return &ParseError{f, "bad EUI64 Address", l}
+               }
+       }
+       addr[14] = l.token[21]
+       addr[15] = l.token[22]
+
+       i, e := strconv.ParseUint(string(addr), 16, 64)
+       if e != nil {
+               return &ParseError{f, "bad EUI68 Address", l}
+       }
+       rr.Address = i
+       return slurpRemainder(c, f)
+}
+
+func (rr *SSHFP) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SSHFP Algorithm", l}
+       }
+       rr.Algorithm = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SSHFP Type", l}
+       }
+       rr.Type = uint8(i)
+       c.Next() // zBlank
+       s, e1 := endingToString(c, "bad SSHFP Fingerprint", f)
+       if e1 != nil {
+               return e1
+       }
+       rr.FingerPrint = s
+       return nil
+}
+
+func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, f, typ string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad " + typ + " Flags", l}
+       }
+       rr.Flags = uint16(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad " + typ + " Protocol", l}
+       }
+       rr.Protocol = uint8(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad " + typ + " Algorithm", l}
+       }
+       rr.Algorithm = uint8(i)
+       s, e1 := endingToString(c, "bad "+typ+" PublicKey", f)
+       if e1 != nil {
+               return e1
+       }
+       rr.PublicKey = s
+       return nil
+}
+
+func (rr *DNSKEY) parse(c *zlexer, o, f string) *ParseError {
+       return rr.parseDNSKEY(c, o, f, "DNSKEY")
+}
+
+func (rr *KEY) parse(c *zlexer, o, f string) *ParseError {
+       return rr.parseDNSKEY(c, o, f, "KEY")
+}
+
+func (rr *CDNSKEY) parse(c *zlexer, o, f string) *ParseError {
+       return rr.parseDNSKEY(c, o, f, "CDNSKEY")
+}
+
+func (rr *RKEY) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad RKEY Flags", l}
+       }
+       rr.Flags = uint16(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad RKEY Protocol", l}
+       }
+       rr.Protocol = uint8(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad RKEY Algorithm", l}
+       }
+       rr.Algorithm = uint8(i)
+       s, e1 := endingToString(c, "bad RKEY PublicKey", f)
+       if e1 != nil {
+               return e1
+       }
+       rr.PublicKey = s
+       return nil
+}
+
+func (rr *EID) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToString(c, "bad EID Endpoint", f)
+       if e != nil {
+               return e
+       }
+       rr.Endpoint = s
+       return nil
+}
+
+func (rr *NIMLOC) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToString(c, "bad NIMLOC Locator", f)
+       if e != nil {
+               return e
+       }
+       rr.Locator = s
+       return nil
+}
+
+func (rr *GPOS) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       _, e := strconv.ParseFloat(l.token, 64)
+       if e != nil || l.err {
+               return &ParseError{f, "bad GPOS Longitude", l}
+       }
+       rr.Longitude = l.token
+       c.Next() // zBlank
+       l, _ = c.Next()
+       _, e = strconv.ParseFloat(l.token, 64)
+       if e != nil || l.err {
+               return &ParseError{f, "bad GPOS Latitude", l}
+       }
+       rr.Latitude = l.token
+       c.Next() // zBlank
+       l, _ = c.Next()
+       _, e = strconv.ParseFloat(l.token, 64)
+       if e != nil || l.err {
+               return &ParseError{f, "bad GPOS Altitude", l}
+       }
+       rr.Altitude = l.token
+       return slurpRemainder(c, f)
+}
+
+func (rr *DS) parseDS(c *zlexer, o, f, typ string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad " + typ + " KeyTag", l}
+       }
+       rr.KeyTag = uint16(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, e = strconv.ParseUint(l.token, 10, 8); e != nil {
+               tokenUpper := strings.ToUpper(l.token)
+               i, ok := StringToAlgorithm[tokenUpper]
+               if !ok || l.err {
+                       return &ParseError{f, "bad " + typ + " Algorithm", l}
+               }
+               rr.Algorithm = i
+       } else {
+               rr.Algorithm = uint8(i)
+       }
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad " + typ + " DigestType", l}
+       }
+       rr.DigestType = uint8(i)
+       s, e1 := endingToString(c, "bad "+typ+" Digest", f)
+       if e1 != nil {
+               return e1
+       }
+       rr.Digest = s
+       return nil
+}
+
+func (rr *DS) parse(c *zlexer, o, f string) *ParseError {
+       return rr.parseDS(c, o, f, "DS")
+}
+
+func (rr *DLV) parse(c *zlexer, o, f string) *ParseError {
+       return rr.parseDS(c, o, f, "DLV")
+}
+
+func (rr *CDS) parse(c *zlexer, o, f string) *ParseError {
+       return rr.parseDS(c, o, f, "CDS")
+}
+
+func (rr *TA) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad TA KeyTag", l}
+       }
+       rr.KeyTag = uint16(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if i, e := strconv.ParseUint(l.token, 10, 8); e != nil {
+               tokenUpper := strings.ToUpper(l.token)
+               i, ok := StringToAlgorithm[tokenUpper]
+               if !ok || l.err {
+                       return &ParseError{f, "bad TA Algorithm", l}
+               }
+               rr.Algorithm = i
+       } else {
+               rr.Algorithm = uint8(i)
+       }
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad TA DigestType", l}
+       }
+       rr.DigestType = uint8(i)
+       s, err := endingToString(c, "bad TA Digest", f)
+       if err != nil {
+               return err
+       }
+       rr.Digest = s
+       return nil
+}
+
+func (rr *TLSA) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad TLSA Usage", l}
+       }
+       rr.Usage = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad TLSA Selector", l}
+       }
+       rr.Selector = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad TLSA MatchingType", l}
+       }
+       rr.MatchingType = uint8(i)
+       // So this needs be e2 (i.e. different than e), because...??t
+       s, e2 := endingToString(c, "bad TLSA Certificate", f)
+       if e2 != nil {
+               return e2
+       }
+       rr.Certificate = s
+       return nil
+}
+
+func (rr *SMIMEA) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SMIMEA Usage", l}
+       }
+       rr.Usage = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SMIMEA Selector", l}
+       }
+       rr.Selector = uint8(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 8)
+       if e != nil || l.err {
+               return &ParseError{f, "bad SMIMEA MatchingType", l}
+       }
+       rr.MatchingType = uint8(i)
+       // So this needs be e2 (i.e. different than e), because...??t
+       s, e2 := endingToString(c, "bad SMIMEA Certificate", f)
+       if e2 != nil {
+               return e2
+       }
+       rr.Certificate = s
+       return nil
+}
+
+func (rr *RFC3597) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if l.token != "\\#" {
+               return &ParseError{f, "bad RFC3597 Rdata", l}
+       }
+
+       c.Next() // zBlank
+       l, _ = c.Next()
+       rdlength, e := strconv.Atoi(l.token)
+       if e != nil || l.err {
+               return &ParseError{f, "bad RFC3597 Rdata ", l}
+       }
+
+       s, e1 := endingToString(c, "bad RFC3597 Rdata", f)
+       if e1 != nil {
+               return e1
+       }
+       if rdlength*2 != len(s) {
+               return &ParseError{f, "bad RFC3597 Rdata", l}
+       }
+       rr.Rdata = s
+       return nil
+}
+
+func (rr *SPF) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToTxtSlice(c, "bad SPF Txt", f)
+       if e != nil {
+               return e
+       }
+       rr.Txt = s
+       return nil
+}
+
+func (rr *AVC) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToTxtSlice(c, "bad AVC Txt", f)
+       if e != nil {
+               return e
+       }
+       rr.Txt = s
+       return nil
+}
+
+func (rr *TXT) parse(c *zlexer, o, f string) *ParseError {
+       // no zBlank reading here, because all this rdata is TXT
+       s, e := endingToTxtSlice(c, "bad TXT Txt", f)
+       if e != nil {
+               return e
+       }
+       rr.Txt = s
+       return nil
+}
+
+// identical to setTXT
+func (rr *NINFO) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToTxtSlice(c, "bad NINFO ZSData", f)
+       if e != nil {
+               return e
+       }
+       rr.ZSData = s
+       return nil
+}
+
+func (rr *URI) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad URI Priority", l}
+       }
+       rr.Priority = uint16(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       i, e = strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad URI Weight", l}
+       }
+       rr.Weight = uint16(i)
+
+       c.Next() // zBlank
+       s, err := endingToTxtSlice(c, "bad URI Target", f)
+       if err != nil {
+               return err
+       }
+       if len(s) != 1 {
+               return &ParseError{f, "bad URI Target", l}
+       }
+       rr.Target = s[0]
+       return nil
+}
+
+func (rr *DHCID) parse(c *zlexer, o, f string) *ParseError {
+       // awesome record to parse!
+       s, e := endingToString(c, "bad DHCID Digest", f)
+       if e != nil {
+               return e
+       }
+       rr.Digest = s
+       return nil
+}
+
+func (rr *NID) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad NID Preference", l}
+       }
+       rr.Preference = uint16(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       u, err := stringToNodeID(l)
+       if err != nil || l.err {
+               return err
+       }
+       rr.NodeID = u
+       return slurpRemainder(c, f)
+}
+
+func (rr *L32) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad L32 Preference", l}
+       }
+       rr.Preference = uint16(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Locator32 = net.ParseIP(l.token)
+       if rr.Locator32 == nil || l.err {
+               return &ParseError{f, "bad L32 Locator", l}
+       }
+       return slurpRemainder(c, f)
+}
+
+func (rr *LP) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad LP Preference", l}
+       }
+       rr.Preference = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Fqdn = l.token
+       name, nameOk := toAbsoluteName(l.token, o)
+       if l.err || !nameOk {
+               return &ParseError{f, "bad LP Fqdn", l}
+       }
+       rr.Fqdn = name
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *L64) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad L64 Preference", l}
+       }
+       rr.Preference = uint16(i)
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       u, err := stringToNodeID(l)
+       if err != nil || l.err {
+               return err
+       }
+       rr.Locator64 = u
+       return slurpRemainder(c, f)
+}
+
+func (rr *UID) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 32)
+       if e != nil || l.err {
+               return &ParseError{f, "bad UID Uid", l}
+       }
+       rr.Uid = uint32(i)
+       return slurpRemainder(c, f)
+}
+
+func (rr *GID) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 32)
+       if e != nil || l.err {
+               return &ParseError{f, "bad GID Gid", l}
+       }
+       rr.Gid = uint32(i)
+       return slurpRemainder(c, f)
+}
+
+func (rr *UINFO) parse(c *zlexer, o, f string) *ParseError {
+       s, e := endingToTxtSlice(c, "bad UINFO Uinfo", f)
+       if e != nil {
+               return e
+       }
+       if ln := len(s); ln == 0 {
+               return nil
+       }
+       rr.Uinfo = s[0] // silently discard anything after the first character-string
+       return nil
+}
+
+func (rr *PX) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return slurpRemainder(c, f)
+       }
+
+       i, e := strconv.ParseUint(l.token, 10, 16)
+       if e != nil || l.err {
+               return &ParseError{f, "bad PX Preference", l}
+       }
+       rr.Preference = uint16(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Map822 = l.token
+       map822, map822Ok := toAbsoluteName(l.token, o)
+       if l.err || !map822Ok {
+               return &ParseError{f, "bad PX Map822", l}
+       }
+       rr.Map822 = map822
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       rr.Mapx400 = l.token
+       mapx400, mapx400Ok := toAbsoluteName(l.token, o)
+       if l.err || !mapx400Ok {
+               return &ParseError{f, "bad PX Mapx400", l}
+       }
+       rr.Mapx400 = mapx400
+
+       return slurpRemainder(c, f)
+}
+
+func (rr *CAA) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+       if len(l.token) == 0 { // dynamic update rr.
+               return nil
+       }
+
+       i, err := strconv.ParseUint(l.token, 10, 8)
+       if err != nil || l.err {
+               return &ParseError{f, "bad CAA Flag", l}
+       }
+       rr.Flag = uint8(i)
+
+       c.Next()        // zBlank
+       l, _ = c.Next() // zString
+       if l.value != zString {
+               return &ParseError{f, "bad CAA Tag", l}
+       }
+       rr.Tag = l.token
+
+       c.Next() // zBlank
+       s, e := endingToTxtSlice(c, "bad CAA Value", f)
+       if e != nil {
+               return e
+       }
+       if len(s) != 1 {
+               return &ParseError{f, "bad CAA Value", l}
+       }
+       rr.Value = s[0]
+       return nil
+}
+
+func (rr *TKEY) parse(c *zlexer, o, f string) *ParseError {
+       l, _ := c.Next()
+
+       // Algorithm
+       if l.value != zString {
+               return &ParseError{f, "bad TKEY algorithm", l}
+       }
+       rr.Algorithm = l.token
+       c.Next() // zBlank
+
+       // Get the key length and key values
+       l, _ = c.Next()
+       i, err := strconv.ParseUint(l.token, 10, 8)
+       if err != nil || l.err {
+               return &ParseError{f, "bad TKEY key length", l}
+       }
+       rr.KeySize = uint16(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if l.value != zString {
+               return &ParseError{f, "bad TKEY key", l}
+       }
+       rr.Key = l.token
+       c.Next() // zBlank
+
+       // Get the otherdata length and string data
+       l, _ = c.Next()
+       i, err = strconv.ParseUint(l.token, 10, 8)
+       if err != nil || l.err {
+               return &ParseError{f, "bad TKEY otherdata length", l}
+       }
+       rr.OtherLen = uint16(i)
+       c.Next() // zBlank
+       l, _ = c.Next()
+       if l.value != zString {
+               return &ParseError{f, "bad TKEY otherday", l}
+       }
+       rr.OtherData = l.token
+
+       return nil
+}
diff --git a/vendor/github.com/miekg/dns/serve_mux.go b/vendor/github.com/miekg/dns/serve_mux.go
new file mode 100644 (file)
index 0000000..19ba0a1
--- /dev/null
@@ -0,0 +1,147 @@
+package dns
+
+import (
+       "bytes"
+       "sync"
+)
+
+// ServeMux is an DNS request multiplexer. It matches the zone name of
+// each incoming request against a list of registered patterns add calls
+// the handler for the pattern that most closely matches the zone name.
+//
+// ServeMux is DNSSEC aware, meaning that queries for the DS record are
+// redirected to the parent zone (if that is also registered), otherwise
+// the child gets the query.
+//
+// ServeMux is also safe for concurrent access from multiple goroutines.
+//
+// The zero ServeMux is empty and ready for use.
+type ServeMux struct {
+       z map[string]Handler
+       m sync.RWMutex
+}
+
+// NewServeMux allocates and returns a new ServeMux.
+func NewServeMux() *ServeMux {
+       return new(ServeMux)
+}
+
+// DefaultServeMux is the default ServeMux used by Serve.
+var DefaultServeMux = NewServeMux()
+
+func (mux *ServeMux) match(q string, t uint16) Handler {
+       mux.m.RLock()
+       defer mux.m.RUnlock()
+       if mux.z == nil {
+               return nil
+       }
+
+       var handler Handler
+
+       // TODO(tmthrgd): Once https://go-review.googlesource.com/c/go/+/137575
+       // lands in a go release, replace the following with strings.ToLower.
+       var sb bytes.Buffer
+       for i := 0; i < len(q); i++ {
+               c := q[i]
+               if !(c >= 'A' && c <= 'Z') {
+                       continue
+               }
+
+               sb.Grow(len(q))
+               sb.WriteString(q[:i])
+
+               for ; i < len(q); i++ {
+                       c := q[i]
+                       if c >= 'A' && c <= 'Z' {
+                               c += 'a' - 'A'
+                       }
+
+                       sb.WriteByte(c)
+               }
+
+               q = sb.String()
+               break
+       }
+
+       for off, end := 0, false; !end; off, end = NextLabel(q, off) {
+               if h, ok := mux.z[q[off:]]; ok {
+                       if t != TypeDS {
+                               return h
+                       }
+                       // Continue for DS to see if we have a parent too, if so delegate to the parent
+                       handler = h
+               }
+       }
+
+       // Wildcard match, if we have found nothing try the root zone as a last resort.
+       if h, ok := mux.z["."]; ok {
+               return h
+       }
+
+       return handler
+}
+
+// Handle adds a handler to the ServeMux for pattern.
+func (mux *ServeMux) Handle(pattern string, handler Handler) {
+       if pattern == "" {
+               panic("dns: invalid pattern " + pattern)
+       }
+       mux.m.Lock()
+       if mux.z == nil {
+               mux.z = make(map[string]Handler)
+       }
+       mux.z[Fqdn(pattern)] = handler
+       mux.m.Unlock()
+}
+
+// HandleFunc adds a handler function to the ServeMux for pattern.
+func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
+       mux.Handle(pattern, HandlerFunc(handler))
+}
+
+// HandleRemove deregisters the handler specific for pattern from the ServeMux.
+func (mux *ServeMux) HandleRemove(pattern string) {
+       if pattern == "" {
+               panic("dns: invalid pattern " + pattern)
+       }
+       mux.m.Lock()
+       delete(mux.z, Fqdn(pattern))
+       mux.m.Unlock()
+}
+
+// ServeDNS dispatches the request to the handler whose pattern most
+// closely matches the request message.
+//
+// ServeDNS is DNSSEC aware, meaning that queries for the DS record
+// are redirected to the parent zone (if that is also registered),
+// otherwise the child gets the query.
+//
+// If no handler is found, or there is no question, a standard SERVFAIL
+// message is returned
+func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) {
+       var h Handler
+       if len(req.Question) >= 1 { // allow more than one question
+               h = mux.match(req.Question[0].Name, req.Question[0].Qtype)
+       }
+
+       if h != nil {
+               h.ServeDNS(w, req)
+       } else {
+               HandleFailed(w, req)
+       }
+}
+
+// Handle registers the handler with the given pattern
+// in the DefaultServeMux. The documentation for
+// ServeMux explains how patterns are matched.
+func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
+
+// HandleRemove deregisters the handle with the given pattern
+// in the DefaultServeMux.
+func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
+
+// HandleFunc registers the handler function with the given pattern
+// in the DefaultServeMux.
+func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
+       DefaultServeMux.HandleFunc(pattern, handler)
+}
diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go
new file mode 100644 (file)
index 0000000..8824037
--- /dev/null
@@ -0,0 +1,866 @@
+// DNS server implementation.
+
+package dns
+
+import (
+       "bytes"
+       "context"
+       "crypto/tls"
+       "encoding/binary"
+       "errors"
+       "io"
+       "net"
+       "strings"
+       "sync"
+       "sync/atomic"
+       "time"
+)
+
+// Default maximum number of TCP queries before we close the socket.
+const maxTCPQueries = 128
+
+// The maximum number of idle workers.
+//
+// This controls the maximum number of workers that are allowed to stay
+// idle waiting for incoming requests before being torn down.
+//
+// If this limit is reached, the server will just keep spawning new
+// workers (goroutines) for each incoming request. In this case, each
+// worker will only be used for a single request.
+const maxIdleWorkersCount = 10000
+
+// The maximum length of time a worker may idle for before being destroyed.
+const idleWorkerTimeout = 10 * time.Second
+
+// aLongTimeAgo is a non-zero time, far in the past, used for
+// immediate cancelation of network operations.
+var aLongTimeAgo = time.Unix(1, 0)
+
+// Handler is implemented by any value that implements ServeDNS.
+type Handler interface {
+       ServeDNS(w ResponseWriter, r *Msg)
+}
+
+// The HandlerFunc type is an adapter to allow the use of
+// ordinary functions as DNS handlers.  If f is a function
+// with the appropriate signature, HandlerFunc(f) is a
+// Handler object that calls f.
+type HandlerFunc func(ResponseWriter, *Msg)
+
+// ServeDNS calls f(w, r).
+func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
+       f(w, r)
+}
+
+// A ResponseWriter interface is used by an DNS handler to
+// construct an DNS response.
+type ResponseWriter interface {
+       // LocalAddr returns the net.Addr of the server
+       LocalAddr() net.Addr
+       // RemoteAddr returns the net.Addr of the client that sent the current request.
+       RemoteAddr() net.Addr
+       // WriteMsg writes a reply back to the client.
+       WriteMsg(*Msg) error
+       // Write writes a raw buffer back to the client.
+       Write([]byte) (int, error)
+       // Close closes the connection.
+       Close() error
+       // TsigStatus returns the status of the Tsig.
+       TsigStatus() error
+       // TsigTimersOnly sets the tsig timers only boolean.
+       TsigTimersOnly(bool)
+       // Hijack lets the caller take over the connection.
+       // After a call to Hijack(), the DNS package will not do anything with the connection.
+       Hijack()
+}
+
+// A ConnectionStater interface is used by a DNS Handler to access TLS connection state
+// when available.
+type ConnectionStater interface {
+       ConnectionState() *tls.ConnectionState
+}
+
+type response struct {
+       msg            []byte
+       closed         bool // connection has been closed
+       hijacked       bool // connection has been hijacked by handler
+       tsigTimersOnly bool
+       tsigStatus     error
+       tsigRequestMAC string
+       tsigSecret     map[string]string // the tsig secrets
+       udp            *net.UDPConn      // i/o connection if UDP was used
+       tcp            net.Conn          // i/o connection if TCP was used
+       udpSession     *SessionUDP       // oob data to get egress interface right
+       writer         Writer            // writer to output the raw DNS bits
+       wg             *sync.WaitGroup   // for gracefull shutdown
+}
+
+// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets.
+func HandleFailed(w ResponseWriter, r *Msg) {
+       m := new(Msg)
+       m.SetRcode(r, RcodeServerFailure)
+       // does not matter if this write fails
+       w.WriteMsg(m)
+}
+
+// ListenAndServe Starts a server on address and network specified Invoke handler
+// for incoming queries.
+func ListenAndServe(addr string, network string, handler Handler) error {
+       server := &Server{Addr: addr, Net: network, Handler: handler}
+       return server.ListenAndServe()
+}
+
+// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in
+// http://golang.org/pkg/net/http/#ListenAndServeTLS
+func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
+       cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+       if err != nil {
+               return err
+       }
+
+       config := tls.Config{
+               Certificates: []tls.Certificate{cert},
+       }
+
+       server := &Server{
+               Addr:      addr,
+               Net:       "tcp-tls",
+               TLSConfig: &config,
+               Handler:   handler,
+       }
+
+       return server.ListenAndServe()
+}
+
+// ActivateAndServe activates a server with a listener from systemd,
+// l and p should not both be non-nil.
+// If both l and p are not nil only p will be used.
+// Invoke handler for incoming queries.
+func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
+       server := &Server{Listener: l, PacketConn: p, Handler: handler}
+       return server.ActivateAndServe()
+}
+
+// Writer writes raw DNS messages; each call to Write should send an entire message.
+type Writer interface {
+       io.Writer
+}
+
+// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message.
+type Reader interface {
+       // ReadTCP reads a raw message from a TCP connection. Implementations may alter
+       // connection properties, for example the read-deadline.
+       ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error)
+       // ReadUDP reads a raw message from a UDP connection. Implementations may alter
+       // connection properties, for example the read-deadline.
+       ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error)
+}
+
+// defaultReader is an adapter for the Server struct that implements the Reader interface
+// using the readTCP and readUDP func of the embedded Server.
+type defaultReader struct {
+       *Server
+}
+
+func (dr defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
+       return dr.readTCP(conn, timeout)
+}
+
+func (dr defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
+       return dr.readUDP(conn, timeout)
+}
+
+// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader.
+// Implementations should never return a nil Reader.
+type DecorateReader func(Reader) Reader
+
+// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer.
+// Implementations should never return a nil Writer.
+type DecorateWriter func(Writer) Writer
+
+// A Server defines parameters for running an DNS server.
+type Server struct {
+       // Address to listen on, ":dns" if empty.
+       Addr string
+       // if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one
+       Net string
+       // TCP Listener to use, this is to aid in systemd's socket activation.
+       Listener net.Listener
+       // TLS connection configuration
+       TLSConfig *tls.Config
+       // UDP "Listener" to use, this is to aid in systemd's socket activation.
+       PacketConn net.PacketConn
+       // Handler to invoke, dns.DefaultServeMux if nil.
+       Handler Handler
+       // Default buffer size to use to read incoming UDP messages. If not set
+       // it defaults to MinMsgSize (512 B).
+       UDPSize int
+       // The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second.
+       ReadTimeout time.Duration
+       // The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second.
+       WriteTimeout time.Duration
+       // TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).
+       IdleTimeout func() time.Duration
+       // Secret(s) for Tsig map[<zonename>]<base64 secret>. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2).
+       TsigSecret map[string]string
+       // If NotifyStartedFunc is set it is called once the server has started listening.
+       NotifyStartedFunc func()
+       // DecorateReader is optional, allows customization of the process that reads raw DNS messages.
+       DecorateReader DecorateReader
+       // DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
+       DecorateWriter DecorateWriter
+       // Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1).
+       MaxTCPQueries int
+       // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address.
+       // It is only supported on go1.11+ and when using ListenAndServe.
+       ReusePort bool
+       // AcceptMsgFunc will check the incoming message and will reject it early in the process.
+       // By default DefaultMsgAcceptFunc will be used.
+       MsgAcceptFunc MsgAcceptFunc
+
+       // UDP packet or TCP connection queue
+       queue chan *response
+       // Workers count
+       workersCount int32
+
+       // Shutdown handling
+       lock     sync.RWMutex
+       started  bool
+       shutdown chan struct{}
+       conns    map[net.Conn]struct{}
+
+       // A pool for UDP message buffers.
+       udpPool sync.Pool
+}
+
+func (srv *Server) isStarted() bool {
+       srv.lock.RLock()
+       started := srv.started
+       srv.lock.RUnlock()
+       return started
+}
+
+func (srv *Server) worker(w *response) {
+       srv.serve(w)
+
+       for {
+               count := atomic.LoadInt32(&srv.workersCount)
+               if count > maxIdleWorkersCount {
+                       return
+               }
+               if atomic.CompareAndSwapInt32(&srv.workersCount, count, count+1) {
+                       break
+               }
+       }
+
+       defer atomic.AddInt32(&srv.workersCount, -1)
+
+       inUse := false
+       timeout := time.NewTimer(idleWorkerTimeout)
+       defer timeout.Stop()
+LOOP:
+       for {
+               select {
+               case w, ok := <-srv.queue:
+                       if !ok {
+                               break LOOP
+                       }
+                       inUse = true
+                       srv.serve(w)
+               case <-timeout.C:
+                       if !inUse {
+                               break LOOP
+                       }
+                       inUse = false
+                       timeout.Reset(idleWorkerTimeout)
+               }
+       }
+}
+
+func (srv *Server) spawnWorker(w *response) {
+       select {
+       case srv.queue <- w:
+       default:
+               go srv.worker(w)
+       }
+}
+
+func makeUDPBuffer(size int) func() interface{} {
+       return func() interface{} {
+               return make([]byte, size)
+       }
+}
+
+func (srv *Server) init() {
+       srv.queue = make(chan *response)
+
+       srv.shutdown = make(chan struct{})
+       srv.conns = make(map[net.Conn]struct{})
+
+       if srv.UDPSize == 0 {
+               srv.UDPSize = MinMsgSize
+       }
+       if srv.MsgAcceptFunc == nil {
+               srv.MsgAcceptFunc = defaultMsgAcceptFunc
+       }
+
+       srv.udpPool.New = makeUDPBuffer(srv.UDPSize)
+}
+
+func unlockOnce(l sync.Locker) func() {
+       var once sync.Once
+       return func() { once.Do(l.Unlock) }
+}
+
+// ListenAndServe starts a nameserver on the configured address in *Server.
+func (srv *Server) ListenAndServe() error {
+       unlock := unlockOnce(&srv.lock)
+       srv.lock.Lock()
+       defer unlock()
+
+       if srv.started {
+               return &Error{err: "server already started"}
+       }
+
+       addr := srv.Addr
+       if addr == "" {
+               addr = ":domain"
+       }
+
+       srv.init()
+       defer close(srv.queue)
+
+       switch srv.Net {
+       case "tcp", "tcp4", "tcp6":
+               l, err := listenTCP(srv.Net, addr, srv.ReusePort)
+               if err != nil {
+                       return err
+               }
+               srv.Listener = l
+               srv.started = true
+               unlock()
+               return srv.serveTCP(l)
+       case "tcp-tls", "tcp4-tls", "tcp6-tls":
+               if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) {
+                       return errors.New("dns: neither Certificates nor GetCertificate set in Config")
+               }
+               network := strings.TrimSuffix(srv.Net, "-tls")
+               l, err := listenTCP(network, addr, srv.ReusePort)
+               if err != nil {
+                       return err
+               }
+               l = tls.NewListener(l, srv.TLSConfig)
+               srv.Listener = l
+               srv.started = true
+               unlock()
+               return srv.serveTCP(l)
+       case "udp", "udp4", "udp6":
+               l, err := listenUDP(srv.Net, addr, srv.ReusePort)
+               if err != nil {
+                       return err
+               }
+               u := l.(*net.UDPConn)
+               if e := setUDPSocketOptions(u); e != nil {
+                       return e
+               }
+               srv.PacketConn = l
+               srv.started = true
+               unlock()
+               return srv.serveUDP(u)
+       }
+       return &Error{err: "bad network"}
+}
+
+// ActivateAndServe starts a nameserver with the PacketConn or Listener
+// configured in *Server. Its main use is to start a server from systemd.
+func (srv *Server) ActivateAndServe() error {
+       unlock := unlockOnce(&srv.lock)
+       srv.lock.Lock()
+       defer unlock()
+
+       if srv.started {
+               return &Error{err: "server already started"}
+       }
+
+       srv.init()
+       defer close(srv.queue)
+
+       pConn := srv.PacketConn
+       l := srv.Listener
+       if pConn != nil {
+               // Check PacketConn interface's type is valid and value
+               // is not nil
+               if t, ok := pConn.(*net.UDPConn); ok && t != nil {
+                       if e := setUDPSocketOptions(t); e != nil {
+                               return e
+                       }
+                       srv.started = true
+                       unlock()
+                       return srv.serveUDP(t)
+               }
+       }
+       if l != nil {
+               srv.started = true
+               unlock()
+               return srv.serveTCP(l)
+       }
+       return &Error{err: "bad listeners"}
+}
+
+// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and
+// ActivateAndServe will return.
+func (srv *Server) Shutdown() error {
+       return srv.ShutdownContext(context.Background())
+}
+
+// ShutdownContext shuts down a server. After a call to ShutdownContext,
+// ListenAndServe and ActivateAndServe will return.
+//
+// A context.Context may be passed to limit how long to wait for connections
+// to terminate.
+func (srv *Server) ShutdownContext(ctx context.Context) error {
+       srv.lock.Lock()
+       if !srv.started {
+               srv.lock.Unlock()
+               return &Error{err: "server not started"}
+       }
+
+       srv.started = false
+
+       if srv.PacketConn != nil {
+               srv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads
+       }
+
+       if srv.Listener != nil {
+               srv.Listener.Close()
+       }
+
+       for rw := range srv.conns {
+               rw.SetReadDeadline(aLongTimeAgo) // Unblock reads
+       }
+
+       srv.lock.Unlock()
+
+       if testShutdownNotify != nil {
+               testShutdownNotify.Broadcast()
+       }
+
+       var ctxErr error
+       select {
+       case <-srv.shutdown:
+       case <-ctx.Done():
+               ctxErr = ctx.Err()
+       }
+
+       if srv.PacketConn != nil {
+               srv.PacketConn.Close()
+       }
+
+       return ctxErr
+}
+
+var testShutdownNotify *sync.Cond
+
+// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
+func (srv *Server) getReadTimeout() time.Duration {
+       if srv.ReadTimeout != 0 {
+               return srv.ReadTimeout
+       }
+       return dnsTimeout
+}
+
+// serveTCP starts a TCP listener for the server.
+func (srv *Server) serveTCP(l net.Listener) error {
+       defer l.Close()
+
+       if srv.NotifyStartedFunc != nil {
+               srv.NotifyStartedFunc()
+       }
+
+       var wg sync.WaitGroup
+       defer func() {
+               wg.Wait()
+               close(srv.shutdown)
+       }()
+
+       for srv.isStarted() {
+               rw, err := l.Accept()
+               if err != nil {
+                       if !srv.isStarted() {
+                               return nil
+                       }
+                       if neterr, ok := err.(net.Error); ok && neterr.Temporary() {
+                               continue
+                       }
+                       return err
+               }
+               srv.lock.Lock()
+               // Track the connection to allow unblocking reads on shutdown.
+               srv.conns[rw] = struct{}{}
+               srv.lock.Unlock()
+               wg.Add(1)
+               srv.spawnWorker(&response{
+                       tsigSecret: srv.TsigSecret,
+                       tcp:        rw,
+                       wg:         &wg,
+               })
+       }
+
+       return nil
+}
+
+// serveUDP starts a UDP listener for the server.
+func (srv *Server) serveUDP(l *net.UDPConn) error {
+       defer l.Close()
+
+       if srv.NotifyStartedFunc != nil {
+               srv.NotifyStartedFunc()
+       }
+
+       reader := Reader(defaultReader{srv})
+       if srv.DecorateReader != nil {
+               reader = srv.DecorateReader(reader)
+       }
+
+       var wg sync.WaitGroup
+       defer func() {
+               wg.Wait()
+               close(srv.shutdown)
+       }()
+
+       rtimeout := srv.getReadTimeout()
+       // deadline is not used here
+       for srv.isStarted() {
+               m, s, err := reader.ReadUDP(l, rtimeout)
+               if err != nil {
+                       if !srv.isStarted() {
+                               return nil
+                       }
+                       if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
+                               continue
+                       }
+                       return err
+               }
+               if len(m) < headerSize {
+                       if cap(m) == srv.UDPSize {
+                               srv.udpPool.Put(m[:srv.UDPSize])
+                       }
+                       continue
+               }
+               wg.Add(1)
+               srv.spawnWorker(&response{
+                       msg:        m,
+                       tsigSecret: srv.TsigSecret,
+                       udp:        l,
+                       udpSession: s,
+                       wg:         &wg,
+               })
+       }
+
+       return nil
+}
+
+func (srv *Server) serve(w *response) {
+       if srv.DecorateWriter != nil {
+               w.writer = srv.DecorateWriter(w)
+       } else {
+               w.writer = w
+       }
+
+       if w.udp != nil {
+               // serve UDP
+               srv.serveDNS(w)
+
+               w.wg.Done()
+               return
+       }
+
+       defer func() {
+               if !w.hijacked {
+                       w.Close()
+               }
+
+               srv.lock.Lock()
+               delete(srv.conns, w.tcp)
+               srv.lock.Unlock()
+
+               w.wg.Done()
+       }()
+
+       reader := Reader(defaultReader{srv})
+       if srv.DecorateReader != nil {
+               reader = srv.DecorateReader(reader)
+       }
+
+       idleTimeout := tcpIdleTimeout
+       if srv.IdleTimeout != nil {
+               idleTimeout = srv.IdleTimeout()
+       }
+
+       timeout := srv.getReadTimeout()
+
+       limit := srv.MaxTCPQueries
+       if limit == 0 {
+               limit = maxTCPQueries
+       }
+
+       for q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ {
+               var err error
+               w.msg, err = reader.ReadTCP(w.tcp, timeout)
+               if err != nil {
+                       // TODO(tmthrgd): handle error
+                       break
+               }
+               srv.serveDNS(w)
+               if w.tcp == nil {
+                       break // Close() was called
+               }
+               if w.hijacked {
+                       break // client will call Close() themselves
+               }
+               // The first read uses the read timeout, the rest use the
+               // idle timeout.
+               timeout = idleTimeout
+       }
+}
+
+func (srv *Server) disposeBuffer(w *response) {
+       if w.udp != nil && cap(w.msg) == srv.UDPSize {
+               srv.udpPool.Put(w.msg[:srv.UDPSize])
+       }
+       w.msg = nil
+}
+
+func (srv *Server) serveDNS(w *response) {
+       dh, off, err := unpackMsgHdr(w.msg, 0)
+       if err != nil {
+               // Let client hang, they are sending crap; any reply can be used to amplify.
+               return
+       }
+
+       req := new(Msg)
+       req.setHdr(dh)
+
+       switch srv.MsgAcceptFunc(dh) {
+       case MsgAccept:
+       case MsgIgnore:
+               return
+       case MsgReject:
+               req.SetRcodeFormatError(req)
+               // Are we allowed to delete any OPT records here?
+               req.Ns, req.Answer, req.Extra = nil, nil, nil
+
+               w.WriteMsg(req)
+               srv.disposeBuffer(w)
+               return
+       }
+
+       if err := req.unpack(dh, w.msg, off); err != nil {
+               req.SetRcodeFormatError(req)
+               req.Ns, req.Answer, req.Extra = nil, nil, nil
+
+               w.WriteMsg(req)
+               srv.disposeBuffer(w)
+               return
+       }
+
+       w.tsigStatus = nil
+       if w.tsigSecret != nil {
+               if t := req.IsTsig(); t != nil {
+                       if secret, ok := w.tsigSecret[t.Hdr.Name]; ok {
+                               w.tsigStatus = TsigVerify(w.msg, secret, "", false)
+                       } else {
+                               w.tsigStatus = ErrSecret
+                       }
+                       w.tsigTimersOnly = false
+                       w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
+               }
+       }
+
+       srv.disposeBuffer(w)
+
+       handler := srv.Handler
+       if handler == nil {
+               handler = DefaultServeMux
+       }
+
+       handler.ServeDNS(w, req) // Writes back to the client
+}
+
+func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) {
+       // If we race with ShutdownContext, the read deadline may
+       // have been set in the distant past to unblock the read
+       // below. We must not override it, otherwise we may block
+       // ShutdownContext.
+       srv.lock.RLock()
+       if srv.started {
+               conn.SetReadDeadline(time.Now().Add(timeout))
+       }
+       srv.lock.RUnlock()
+
+       l := make([]byte, 2)
+       n, err := conn.Read(l)
+       if err != nil || n != 2 {
+               if err != nil {
+                       return nil, err
+               }
+               return nil, ErrShortRead
+       }
+       length := binary.BigEndian.Uint16(l)
+       if length == 0 {
+               return nil, ErrShortRead
+       }
+       m := make([]byte, int(length))
+       n, err = conn.Read(m[:int(length)])
+       if err != nil || n == 0 {
+               if err != nil {
+                       return nil, err
+               }
+               return nil, ErrShortRead
+       }
+       i := n
+       for i < int(length) {
+               j, err := conn.Read(m[i:int(length)])
+               if err != nil {
+                       return nil, err
+               }
+               i += j
+       }
+       n = i
+       m = m[:n]
+       return m, nil
+}
+
+func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
+       srv.lock.RLock()
+       if srv.started {
+               // See the comment in readTCP above.
+               conn.SetReadDeadline(time.Now().Add(timeout))
+       }
+       srv.lock.RUnlock()
+
+       m := srv.udpPool.Get().([]byte)
+       n, s, err := ReadFromSessionUDP(conn, m)
+       if err != nil {
+               srv.udpPool.Put(m)
+               return nil, nil, err
+       }
+       m = m[:n]
+       return m, s, nil
+}
+
+// WriteMsg implements the ResponseWriter.WriteMsg method.
+func (w *response) WriteMsg(m *Msg) (err error) {
+       if w.closed {
+               return &Error{err: "WriteMsg called after Close"}
+       }
+
+       var data []byte
+       if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
+               if t := m.IsTsig(); t != nil {
+                       data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly)
+                       if err != nil {
+                               return err
+                       }
+                       _, err = w.writer.Write(data)
+                       return err
+               }
+       }
+       data, err = m.Pack()
+       if err != nil {
+               return err
+       }
+       _, err = w.writer.Write(data)
+       return err
+}
+
+// Write implements the ResponseWriter.Write method.
+func (w *response) Write(m []byte) (int, error) {
+       if w.closed {
+               return 0, &Error{err: "Write called after Close"}
+       }
+
+       switch {
+       case w.udp != nil:
+               return WriteToSessionUDP(w.udp, m, w.udpSession)
+       case w.tcp != nil:
+               lm := len(m)
+               if lm < 2 {
+                       return 0, io.ErrShortBuffer
+               }
+               if lm > MaxMsgSize {
+                       return 0, &Error{err: "message too large"}
+               }
+               l := make([]byte, 2, 2+lm)
+               binary.BigEndian.PutUint16(l, uint16(lm))
+               m = append(l, m...)
+
+               n, err := io.Copy(w.tcp, bytes.NewReader(m))
+               return int(n), err
+       default:
+               panic("dns: internal error: udp and tcp both nil")
+       }
+}
+
+// LocalAddr implements the ResponseWriter.LocalAddr method.
+func (w *response) LocalAddr() net.Addr {
+       switch {
+       case w.udp != nil:
+               return w.udp.LocalAddr()
+       case w.tcp != nil:
+               return w.tcp.LocalAddr()
+       default:
+               panic("dns: internal error: udp and tcp both nil")
+       }
+}
+
+// RemoteAddr implements the ResponseWriter.RemoteAddr method.
+func (w *response) RemoteAddr() net.Addr {
+       switch {
+       case w.udpSession != nil:
+               return w.udpSession.RemoteAddr()
+       case w.tcp != nil:
+               return w.tcp.RemoteAddr()
+       default:
+               panic("dns: internal error: udpSession and tcp both nil")
+       }
+}
+
+// TsigStatus implements the ResponseWriter.TsigStatus method.
+func (w *response) TsigStatus() error { return w.tsigStatus }
+
+// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
+func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
+
+// Hijack implements the ResponseWriter.Hijack method.
+func (w *response) Hijack() { w.hijacked = true }
+
+// Close implements the ResponseWriter.Close method
+func (w *response) Close() error {
+       if w.closed {
+               return &Error{err: "connection already closed"}
+       }
+       w.closed = true
+
+       switch {
+       case w.udp != nil:
+               // Can't close the udp conn, as that is actually the listener.
+               return nil
+       case w.tcp != nil:
+               return w.tcp.Close()
+       default:
+               panic("dns: internal error: udp and tcp both nil")
+       }
+}
+
+// ConnectionState() implements the ConnectionStater.ConnectionState() interface.
+func (w *response) ConnectionState() *tls.ConnectionState {
+       type tlsConnectionStater interface {
+               ConnectionState() tls.ConnectionState
+       }
+       if v, ok := w.tcp.(tlsConnectionStater); ok {
+               t := v.ConnectionState()
+               return &t
+       }
+       return nil
+}
diff --git a/vendor/github.com/miekg/dns/sig0.go b/vendor/github.com/miekg/dns/sig0.go
new file mode 100644 (file)
index 0000000..ec65dd7
--- /dev/null
@@ -0,0 +1,213 @@
+package dns
+
+import (
+       "crypto"
+       "crypto/dsa"
+       "crypto/ecdsa"
+       "crypto/rsa"
+       "encoding/binary"
+       "math/big"
+       "strings"
+       "time"
+)
+
+// Sign signs a dns.Msg. It fills the signature with the appropriate data.
+// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
+// and Expiration set.
+func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
+       if k == nil {
+               return nil, ErrPrivKey
+       }
+       if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
+               return nil, ErrKey
+       }
+
+       rr.Hdr = RR_Header{Name: ".", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0}
+       rr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0
+
+       buf := make([]byte, m.Len()+Len(rr))
+       mbuf, err := m.PackBuffer(buf)
+       if err != nil {
+               return nil, err
+       }
+       if &buf[0] != &mbuf[0] {
+               return nil, ErrBuf
+       }
+       off, err := PackRR(rr, buf, len(mbuf), nil, false)
+       if err != nil {
+               return nil, err
+       }
+       buf = buf[:off:cap(buf)]
+
+       hash, ok := AlgorithmToHash[rr.Algorithm]
+       if !ok {
+               return nil, ErrAlg
+       }
+
+       hasher := hash.New()
+       // Write SIG rdata
+       hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
+       // Write message
+       hasher.Write(buf[:len(mbuf)])
+
+       signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
+       if err != nil {
+               return nil, err
+       }
+
+       rr.Signature = toBase64(signature)
+
+       buf = append(buf, signature...)
+       if len(buf) > int(^uint16(0)) {
+               return nil, ErrBuf
+       }
+       // Adjust sig data length
+       rdoff := len(mbuf) + 1 + 2 + 2 + 4
+       rdlen := binary.BigEndian.Uint16(buf[rdoff:])
+       rdlen += uint16(len(signature))
+       binary.BigEndian.PutUint16(buf[rdoff:], rdlen)
+       // Adjust additional count
+       adc := binary.BigEndian.Uint16(buf[10:])
+       adc++
+       binary.BigEndian.PutUint16(buf[10:], adc)
+       return buf, nil
+}
+
+// Verify validates the message buf using the key k.
+// It's assumed that buf is a valid message from which rr was unpacked.
+func (rr *SIG) Verify(k *KEY, buf []byte) error {
+       if k == nil {
+               return ErrKey
+       }
+       if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
+               return ErrKey
+       }
+
+       var hash crypto.Hash
+       switch rr.Algorithm {
+       case DSA, RSASHA1:
+               hash = crypto.SHA1
+       case RSASHA256, ECDSAP256SHA256:
+               hash = crypto.SHA256
+       case ECDSAP384SHA384:
+               hash = crypto.SHA384
+       case RSASHA512:
+               hash = crypto.SHA512
+       default:
+               return ErrAlg
+       }
+       hasher := hash.New()
+
+       buflen := len(buf)
+       qdc := binary.BigEndian.Uint16(buf[4:])
+       anc := binary.BigEndian.Uint16(buf[6:])
+       auc := binary.BigEndian.Uint16(buf[8:])
+       adc := binary.BigEndian.Uint16(buf[10:])
+       offset := headerSize
+       var err error
+       for i := uint16(0); i < qdc && offset < buflen; i++ {
+               _, offset, err = UnpackDomainName(buf, offset)
+               if err != nil {
+                       return err
+               }
+               // Skip past Type and Class
+               offset += 2 + 2
+       }
+       for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
+               _, offset, err = UnpackDomainName(buf, offset)
+               if err != nil {
+                       return err
+               }
+               // Skip past Type, Class and TTL
+               offset += 2 + 2 + 4
+               if offset+1 >= buflen {
+                       continue
+               }
+               rdlen := binary.BigEndian.Uint16(buf[offset:])
+               offset += 2
+               offset += int(rdlen)
+       }
+       if offset >= buflen {
+               return &Error{err: "overflowing unpacking signed message"}
+       }
+
+       // offset should be just prior to SIG
+       bodyend := offset
+       // owner name SHOULD be root
+       _, offset, err = UnpackDomainName(buf, offset)
+       if err != nil {
+               return err
+       }
+       // Skip Type, Class, TTL, RDLen
+       offset += 2 + 2 + 4 + 2
+       sigstart := offset
+       // Skip Type Covered, Algorithm, Labels, Original TTL
+       offset += 2 + 1 + 1 + 4
+       if offset+4+4 >= buflen {
+               return &Error{err: "overflow unpacking signed message"}
+       }
+       expire := binary.BigEndian.Uint32(buf[offset:])
+       offset += 4
+       incept := binary.BigEndian.Uint32(buf[offset:])
+       offset += 4
+       now := uint32(time.Now().Unix())
+       if now < incept || now > expire {
+               return ErrTime
+       }
+       // Skip key tag
+       offset += 2
+       var signername string
+       signername, offset, err = UnpackDomainName(buf, offset)
+       if err != nil {
+               return err
+       }
+       // If key has come from the DNS name compression might
+       // have mangled the case of the name
+       if !strings.EqualFold(signername, k.Header().Name) {
+               return &Error{err: "signer name doesn't match key name"}
+       }
+       sigend := offset
+       hasher.Write(buf[sigstart:sigend])
+       hasher.Write(buf[:10])
+       hasher.Write([]byte{
+               byte((adc - 1) << 8),
+               byte(adc - 1),
+       })
+       hasher.Write(buf[12:bodyend])
+
+       hashed := hasher.Sum(nil)
+       sig := buf[sigend:]
+       switch k.Algorithm {
+       case DSA:
+               pk := k.publicKeyDSA()
+               sig = sig[1:]
+               r := big.NewInt(0)
+               r.SetBytes(sig[:len(sig)/2])
+               s := big.NewInt(0)
+               s.SetBytes(sig[len(sig)/2:])
+               if pk != nil {
+                       if dsa.Verify(pk, hashed, r, s) {
+                               return nil
+                       }
+                       return ErrSig
+               }
+       case RSASHA1, RSASHA256, RSASHA512:
+               pk := k.publicKeyRSA()
+               if pk != nil {
+                       return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
+               }
+       case ECDSAP256SHA256, ECDSAP384SHA384:
+               pk := k.publicKeyECDSA()
+               r := big.NewInt(0)
+               r.SetBytes(sig[:len(sig)/2])
+               s := big.NewInt(0)
+               s.SetBytes(sig[len(sig)/2:])
+               if pk != nil {
+                       if ecdsa.Verify(pk, hashed, r, s) {
+                               return nil
+                       }
+                       return ErrSig
+               }
+       }
+       return ErrKeyAlg
+}
diff --git a/vendor/github.com/miekg/dns/singleinflight.go b/vendor/github.com/miekg/dns/singleinflight.go
new file mode 100644 (file)
index 0000000..febcc30
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2013 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Adapted for dns package usage by Miek Gieben.
+
+package dns
+
+import "sync"
+import "time"
+
+// call is an in-flight or completed singleflight.Do call
+type call struct {
+       wg   sync.WaitGroup
+       val  *Msg
+       rtt  time.Duration
+       err  error
+       dups int
+}
+
+// singleflight represents a class of work and forms a namespace in
+// which units of work can be executed with duplicate suppression.
+type singleflight struct {
+       sync.Mutex                  // protects m
+       m          map[string]*call // lazily initialized
+
+       dontDeleteForTesting bool // this is only to be used by TestConcurrentExchanges
+}
+
+// Do executes and returns the results of the given function, making
+// sure that only one execution is in-flight for a given key at a
+// time. If a duplicate comes in, the duplicate caller waits for the
+// original to complete and receives the same results.
+// The return value shared indicates whether v was given to multiple callers.
+func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) {
+       g.Lock()
+       if g.m == nil {
+               g.m = make(map[string]*call)
+       }
+       if c, ok := g.m[key]; ok {
+               c.dups++
+               g.Unlock()
+               c.wg.Wait()
+               return c.val, c.rtt, c.err, true
+       }
+       c := new(call)
+       c.wg.Add(1)
+       g.m[key] = c
+       g.Unlock()
+
+       c.val, c.rtt, c.err = fn()
+       c.wg.Done()
+
+       if !g.dontDeleteForTesting {
+               g.Lock()
+               delete(g.m, key)
+               g.Unlock()
+       }
+
+       return c.val, c.rtt, c.err, c.dups > 0
+}
diff --git a/vendor/github.com/miekg/dns/smimea.go b/vendor/github.com/miekg/dns/smimea.go
new file mode 100644 (file)
index 0000000..89f09f0
--- /dev/null
@@ -0,0 +1,44 @@
+package dns
+
+import (
+       "crypto/sha256"
+       "crypto/x509"
+       "encoding/hex"
+)
+
+// Sign creates a SMIMEA record from an SSL certificate.
+func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
+       r.Hdr.Rrtype = TypeSMIMEA
+       r.Usage = uint8(usage)
+       r.Selector = uint8(selector)
+       r.MatchingType = uint8(matchingType)
+
+       r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
+       return err
+}
+
+// Verify verifies a SMIMEA record against an SSL certificate. If it is OK
+// a nil error is returned.
+func (r *SMIMEA) Verify(cert *x509.Certificate) error {
+       c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
+       if err != nil {
+               return err // Not also ErrSig?
+       }
+       if r.Certificate == c {
+               return nil
+       }
+       return ErrSig // ErrSig, really?
+}
+
+// SMIMEAName returns the ownername of a SMIMEA resource record as per the
+// format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3
+func SMIMEAName(email, domain string) (string, error) {
+       hasher := sha256.New()
+       hasher.Write([]byte(email))
+
+       // RFC Section 3: "The local-part is hashed using the SHA2-256
+       // algorithm with the hash truncated to 28 octets and
+       // represented in its hexadecimal representation to become the
+       // left-most label in the prepared domain name"
+       return hex.EncodeToString(hasher.Sum(nil)[:28]) + "." + "_smimecert." + domain, nil
+}
diff --git a/vendor/github.com/miekg/dns/tlsa.go b/vendor/github.com/miekg/dns/tlsa.go
new file mode 100644 (file)
index 0000000..4e07983
--- /dev/null
@@ -0,0 +1,44 @@
+package dns
+
+import (
+       "crypto/x509"
+       "net"
+       "strconv"
+)
+
+// Sign creates a TLSA record from an SSL certificate.
+func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
+       r.Hdr.Rrtype = TypeTLSA
+       r.Usage = uint8(usage)
+       r.Selector = uint8(selector)
+       r.MatchingType = uint8(matchingType)
+
+       r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
+       return err
+}
+
+// Verify verifies a TLSA record against an SSL certificate. If it is OK
+// a nil error is returned.
+func (r *TLSA) Verify(cert *x509.Certificate) error {
+       c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
+       if err != nil {
+               return err // Not also ErrSig?
+       }
+       if r.Certificate == c {
+               return nil
+       }
+       return ErrSig // ErrSig, really?
+}
+
+// TLSAName returns the ownername of a TLSA resource record as per the
+// rules specified in RFC 6698, Section 3.
+func TLSAName(name, service, network string) (string, error) {
+       if !IsFqdn(name) {
+               return "", ErrFqdn
+       }
+       p, err := net.LookupPort(network, service)
+       if err != nil {
+               return "", err
+       }
+       return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil
+}
diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go
new file mode 100644 (file)
index 0000000..afa462f
--- /dev/null
@@ -0,0 +1,389 @@
+package dns
+
+import (
+       "crypto/hmac"
+       "crypto/md5"
+       "crypto/sha1"
+       "crypto/sha256"
+       "crypto/sha512"
+       "encoding/binary"
+       "encoding/hex"
+       "hash"
+       "strconv"
+       "strings"
+       "time"
+)
+
+// HMAC hashing codes. These are transmitted as domain names.
+const (
+       HmacMD5    = "hmac-md5.sig-alg.reg.int."
+       HmacSHA1   = "hmac-sha1."
+       HmacSHA256 = "hmac-sha256."
+       HmacSHA512 = "hmac-sha512."
+)
+
+// TSIG is the RR the holds the transaction signature of a message.
+// See RFC 2845 and RFC 4635.
+type TSIG struct {
+       Hdr        RR_Header
+       Algorithm  string `dns:"domain-name"`
+       TimeSigned uint64 `dns:"uint48"`
+       Fudge      uint16
+       MACSize    uint16
+       MAC        string `dns:"size-hex:MACSize"`
+       OrigId     uint16
+       Error      uint16
+       OtherLen   uint16
+       OtherData  string `dns:"size-hex:OtherLen"`
+}
+
+// TSIG has no official presentation format, but this will suffice.
+
+func (rr *TSIG) String() string {
+       s := "\n;; TSIG PSEUDOSECTION:\n"
+       s += rr.Hdr.String() +
+               " " + rr.Algorithm +
+               " " + tsigTimeToString(rr.TimeSigned) +
+               " " + strconv.Itoa(int(rr.Fudge)) +
+               " " + strconv.Itoa(int(rr.MACSize)) +
+               " " + strings.ToUpper(rr.MAC) +
+               " " + strconv.Itoa(int(rr.OrigId)) +
+               " " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
+               " " + strconv.Itoa(int(rr.OtherLen)) +
+               " " + rr.OtherData
+       return s
+}
+
+func (rr *TSIG) parse(c *zlexer, origin, file string) *ParseError {
+       panic("dns: internal error: parse should never be called on TSIG")
+}
+
+// The following values must be put in wireformat, so that the MAC can be calculated.
+// RFC 2845, section 3.4.2. TSIG Variables.
+type tsigWireFmt struct {
+       // From RR_Header
+       Name  string `dns:"domain-name"`
+       Class uint16
+       Ttl   uint32
+       // Rdata of the TSIG
+       Algorithm  string `dns:"domain-name"`
+       TimeSigned uint64 `dns:"uint48"`
+       Fudge      uint16
+       // MACSize, MAC and OrigId excluded
+       Error     uint16
+       OtherLen  uint16
+       OtherData string `dns:"size-hex:OtherLen"`
+}
+
+// If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC
+type macWireFmt struct {
+       MACSize uint16
+       MAC     string `dns:"size-hex:MACSize"`
+}
+
+// 3.3. Time values used in TSIG calculations
+type timerWireFmt struct {
+       TimeSigned uint64 `dns:"uint48"`
+       Fudge      uint16
+}
+
+// TsigGenerate fills out the TSIG record attached to the message.
+// The message should contain
+// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
+// time fudge (defaults to 300 seconds) and the current time
+// The TSIG MAC is saved in that Tsig RR.
+// When TsigGenerate is called for the first time requestMAC is set to the empty string and
+// timersOnly is false.
+// If something goes wrong an error is returned, otherwise it is nil.
+func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
+       if m.IsTsig() == nil {
+               panic("dns: TSIG not last RR in additional")
+       }
+       // If we barf here, the caller is to blame
+       rawsecret, err := fromBase64([]byte(secret))
+       if err != nil {
+               return nil, "", err
+       }
+
+       rr := m.Extra[len(m.Extra)-1].(*TSIG)
+       m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
+       mbuf, err := m.Pack()
+       if err != nil {
+               return nil, "", err
+       }
+       buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
+
+       t := new(TSIG)
+       var h hash.Hash
+       switch strings.ToLower(rr.Algorithm) {
+       case HmacMD5:
+               h = hmac.New(md5.New, rawsecret)
+       case HmacSHA1:
+               h = hmac.New(sha1.New, rawsecret)
+       case HmacSHA256:
+               h = hmac.New(sha256.New, rawsecret)
+       case HmacSHA512:
+               h = hmac.New(sha512.New, rawsecret)
+       default:
+               return nil, "", ErrKeyAlg
+       }
+       h.Write(buf)
+       t.MAC = hex.EncodeToString(h.Sum(nil))
+       t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
+
+       t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
+       t.Fudge = rr.Fudge
+       t.TimeSigned = rr.TimeSigned
+       t.Algorithm = rr.Algorithm
+       t.OrigId = m.Id
+
+       tbuf := make([]byte, Len(t))
+       off, err := PackRR(t, tbuf, 0, nil, false)
+       if err != nil {
+               return nil, "", err
+       }
+       mbuf = append(mbuf, tbuf[:off]...)
+       // Update the ArCount directly in the buffer.
+       binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1))
+
+       return mbuf, t.MAC, nil
+}
+
+// TsigVerify verifies the TSIG on a message.
+// If the signature does not validate err contains the
+// error, otherwise it is nil.
+func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
+       rawsecret, err := fromBase64([]byte(secret))
+       if err != nil {
+               return err
+       }
+       // Strip the TSIG from the incoming msg
+       stripped, tsig, err := stripTsig(msg)
+       if err != nil {
+               return err
+       }
+
+       msgMAC, err := hex.DecodeString(tsig.MAC)
+       if err != nil {
+               return err
+       }
+
+       buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
+
+       // Fudge factor works both ways. A message can arrive before it was signed because
+       // of clock skew.
+       now := uint64(time.Now().Unix())
+       ti := now - tsig.TimeSigned
+       if now < tsig.TimeSigned {
+               ti = tsig.TimeSigned - now
+       }
+       if uint64(tsig.Fudge) < ti {
+               return ErrTime
+       }
+
+       var h hash.Hash
+       switch strings.ToLower(tsig.Algorithm) {
+       case HmacMD5:
+               h = hmac.New(md5.New, rawsecret)
+       case HmacSHA1:
+               h = hmac.New(sha1.New, rawsecret)
+       case HmacSHA256:
+               h = hmac.New(sha256.New, rawsecret)
+       case HmacSHA512:
+               h = hmac.New(sha512.New, rawsecret)
+       default:
+               return ErrKeyAlg
+       }
+       h.Write(buf)
+       if !hmac.Equal(h.Sum(nil), msgMAC) {
+               return ErrSig
+       }
+       return nil
+}
+
+// Create a wiredata buffer for the MAC calculation.
+func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
+       var buf []byte
+       if rr.TimeSigned == 0 {
+               rr.TimeSigned = uint64(time.Now().Unix())
+       }
+       if rr.Fudge == 0 {
+               rr.Fudge = 300 // Standard (RFC) default.
+       }
+
+       // Replace message ID in header with original ID from TSIG
+       binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId)
+
+       if requestMAC != "" {
+               m := new(macWireFmt)
+               m.MACSize = uint16(len(requestMAC) / 2)
+               m.MAC = requestMAC
+               buf = make([]byte, len(requestMAC)) // long enough
+               n, _ := packMacWire(m, buf)
+               buf = buf[:n]
+       }
+
+       tsigvar := make([]byte, DefaultMsgSize)
+       if timersOnly {
+               tsig := new(timerWireFmt)
+               tsig.TimeSigned = rr.TimeSigned
+               tsig.Fudge = rr.Fudge
+               n, _ := packTimerWire(tsig, tsigvar)
+               tsigvar = tsigvar[:n]
+       } else {
+               tsig := new(tsigWireFmt)
+               tsig.Name = strings.ToLower(rr.Hdr.Name)
+               tsig.Class = ClassANY
+               tsig.Ttl = rr.Hdr.Ttl
+               tsig.Algorithm = strings.ToLower(rr.Algorithm)
+               tsig.TimeSigned = rr.TimeSigned
+               tsig.Fudge = rr.Fudge
+               tsig.Error = rr.Error
+               tsig.OtherLen = rr.OtherLen
+               tsig.OtherData = rr.OtherData
+               n, _ := packTsigWire(tsig, tsigvar)
+               tsigvar = tsigvar[:n]
+       }
+
+       if requestMAC != "" {
+               x := append(buf, msgbuf...)
+               buf = append(x, tsigvar...)
+       } else {
+               buf = append(msgbuf, tsigvar...)
+       }
+       return buf
+}
+
+// Strip the TSIG from the raw message.
+func stripTsig(msg []byte) ([]byte, *TSIG, error) {
+       // Copied from msg.go's Unpack() Header, but modified.
+       var (
+               dh  Header
+               err error
+       )
+       off, tsigoff := 0, 0
+
+       if dh, off, err = unpackMsgHdr(msg, off); err != nil {
+               return nil, nil, err
+       }
+       if dh.Arcount == 0 {
+               return nil, nil, ErrNoSig
+       }
+
+       // Rcode, see msg.go Unpack()
+       if int(dh.Bits&0xF) == RcodeNotAuth {
+               return nil, nil, ErrAuth
+       }
+
+       for i := 0; i < int(dh.Qdcount); i++ {
+               _, off, err = unpackQuestion(msg, off)
+               if err != nil {
+                       return nil, nil, err
+               }
+       }
+
+       _, off, err = unpackRRslice(int(dh.Ancount), msg, off)
+       if err != nil {
+               return nil, nil, err
+       }
+       _, off, err = unpackRRslice(int(dh.Nscount), msg, off)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       rr := new(TSIG)
+       var extra RR
+       for i := 0; i < int(dh.Arcount); i++ {
+               tsigoff = off
+               extra, off, err = UnpackRR(msg, off)
+               if err != nil {
+                       return nil, nil, err
+               }
+               if extra.Header().Rrtype == TypeTSIG {
+                       rr = extra.(*TSIG)
+                       // Adjust Arcount.
+                       arcount := binary.BigEndian.Uint16(msg[10:])
+                       binary.BigEndian.PutUint16(msg[10:], arcount-1)
+                       break
+               }
+       }
+       if rr == nil {
+               return nil, nil, ErrNoSig
+       }
+       return msg[:tsigoff], rr, nil
+}
+
+// Translate the TSIG time signed into a date. There is no
+// need for RFC1982 calculations as this date is 48 bits.
+func tsigTimeToString(t uint64) string {
+       ti := time.Unix(int64(t), 0).UTC()
+       return ti.Format("20060102150405")
+}
+
+func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) {
+       // copied from zmsg.go TSIG packing
+       // RR_Header
+       off, err := PackDomainName(tw.Name, msg, 0, nil, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(tw.Class, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(tw.Ttl, msg, off)
+       if err != nil {
+               return off, err
+       }
+
+       off, err = PackDomainName(tw.Algorithm, msg, off, nil, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint48(tw.TimeSigned, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(tw.Fudge, msg, off)
+       if err != nil {
+               return off, err
+       }
+
+       off, err = packUint16(tw.Error, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(tw.OtherLen, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(tw.OtherData, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func packMacWire(mw *macWireFmt, msg []byte) (int, error) {
+       off, err := packUint16(mw.MACSize, msg, 0)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(mw.MAC, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) {
+       off, err := packUint48(tw.TimeSigned, msg, 0)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(tw.Fudge, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go
new file mode 100644 (file)
index 0000000..095ffe3
--- /dev/null
@@ -0,0 +1,1435 @@
+package dns
+
+import (
+       "bytes"
+       "fmt"
+       "net"
+       "strconv"
+       "strings"
+       "time"
+)
+
+type (
+       // Type is a DNS type.
+       Type uint16
+       // Class is a DNS class.
+       Class uint16
+       // Name is a DNS domain name.
+       Name string
+)
+
+// Packet formats
+
+// Wire constants and supported types.
+const (
+       // valid RR_Header.Rrtype and Question.qtype
+
+       TypeNone       uint16 = 0
+       TypeA          uint16 = 1
+       TypeNS         uint16 = 2
+       TypeMD         uint16 = 3
+       TypeMF         uint16 = 4
+       TypeCNAME      uint16 = 5
+       TypeSOA        uint16 = 6
+       TypeMB         uint16 = 7
+       TypeMG         uint16 = 8
+       TypeMR         uint16 = 9
+       TypeNULL       uint16 = 10
+       TypePTR        uint16 = 12
+       TypeHINFO      uint16 = 13
+       TypeMINFO      uint16 = 14
+       TypeMX         uint16 = 15
+       TypeTXT        uint16 = 16
+       TypeRP         uint16 = 17
+       TypeAFSDB      uint16 = 18
+       TypeX25        uint16 = 19
+       TypeISDN       uint16 = 20
+       TypeRT         uint16 = 21
+       TypeNSAPPTR    uint16 = 23
+       TypeSIG        uint16 = 24
+       TypeKEY        uint16 = 25
+       TypePX         uint16 = 26
+       TypeGPOS       uint16 = 27
+       TypeAAAA       uint16 = 28
+       TypeLOC        uint16 = 29
+       TypeNXT        uint16 = 30
+       TypeEID        uint16 = 31
+       TypeNIMLOC     uint16 = 32
+       TypeSRV        uint16 = 33
+       TypeATMA       uint16 = 34
+       TypeNAPTR      uint16 = 35
+       TypeKX         uint16 = 36
+       TypeCERT       uint16 = 37
+       TypeDNAME      uint16 = 39
+       TypeOPT        uint16 = 41 // EDNS
+       TypeDS         uint16 = 43
+       TypeSSHFP      uint16 = 44
+       TypeRRSIG      uint16 = 46
+       TypeNSEC       uint16 = 47
+       TypeDNSKEY     uint16 = 48
+       TypeDHCID      uint16 = 49
+       TypeNSEC3      uint16 = 50
+       TypeNSEC3PARAM uint16 = 51
+       TypeTLSA       uint16 = 52
+       TypeSMIMEA     uint16 = 53
+       TypeHIP        uint16 = 55
+       TypeNINFO      uint16 = 56
+       TypeRKEY       uint16 = 57
+       TypeTALINK     uint16 = 58
+       TypeCDS        uint16 = 59
+       TypeCDNSKEY    uint16 = 60
+       TypeOPENPGPKEY uint16 = 61
+       TypeCSYNC      uint16 = 62
+       TypeSPF        uint16 = 99
+       TypeUINFO      uint16 = 100
+       TypeUID        uint16 = 101
+       TypeGID        uint16 = 102
+       TypeUNSPEC     uint16 = 103
+       TypeNID        uint16 = 104
+       TypeL32        uint16 = 105
+       TypeL64        uint16 = 106
+       TypeLP         uint16 = 107
+       TypeEUI48      uint16 = 108
+       TypeEUI64      uint16 = 109
+       TypeURI        uint16 = 256
+       TypeCAA        uint16 = 257
+       TypeAVC        uint16 = 258
+
+       TypeTKEY uint16 = 249
+       TypeTSIG uint16 = 250
+
+       // valid Question.Qtype only
+       TypeIXFR  uint16 = 251
+       TypeAXFR  uint16 = 252
+       TypeMAILB uint16 = 253
+       TypeMAILA uint16 = 254
+       TypeANY   uint16 = 255
+
+       TypeTA       uint16 = 32768
+       TypeDLV      uint16 = 32769
+       TypeReserved uint16 = 65535
+
+       // valid Question.Qclass
+       ClassINET   = 1
+       ClassCSNET  = 2
+       ClassCHAOS  = 3
+       ClassHESIOD = 4
+       ClassNONE   = 254
+       ClassANY    = 255
+
+       // Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+       RcodeSuccess        = 0  // NoError   - No Error                          [DNS]
+       RcodeFormatError    = 1  // FormErr   - Format Error                      [DNS]
+       RcodeServerFailure  = 2  // ServFail  - Server Failure                    [DNS]
+       RcodeNameError      = 3  // NXDomain  - Non-Existent Domain               [DNS]
+       RcodeNotImplemented = 4  // NotImp    - Not Implemented                   [DNS]
+       RcodeRefused        = 5  // Refused   - Query Refused                     [DNS]
+       RcodeYXDomain       = 6  // YXDomain  - Name Exists when it should not    [DNS Update]
+       RcodeYXRrset        = 7  // YXRRSet   - RR Set Exists when it should not  [DNS Update]
+       RcodeNXRrset        = 8  // NXRRSet   - RR Set that should exist does not [DNS Update]
+       RcodeNotAuth        = 9  // NotAuth   - Server Not Authoritative for zone [DNS Update]
+       RcodeNotZone        = 10 // NotZone   - Name not contained in zone        [DNS Update/TSIG]
+       RcodeBadSig         = 16 // BADSIG    - TSIG Signature Failure            [TSIG]
+       RcodeBadVers        = 16 // BADVERS   - Bad OPT Version                   [EDNS0]
+       RcodeBadKey         = 17 // BADKEY    - Key not recognized                [TSIG]
+       RcodeBadTime        = 18 // BADTIME   - Signature out of time window      [TSIG]
+       RcodeBadMode        = 19 // BADMODE   - Bad TKEY Mode                     [TKEY]
+       RcodeBadName        = 20 // BADNAME   - Duplicate key name                [TKEY]
+       RcodeBadAlg         = 21 // BADALG    - Algorithm not supported           [TKEY]
+       RcodeBadTrunc       = 22 // BADTRUNC  - Bad Truncation                    [TSIG]
+       RcodeBadCookie      = 23 // BADCOOKIE - Bad/missing Server Cookie         [DNS Cookies]
+
+       // Message Opcodes. There is no 3.
+       OpcodeQuery  = 0
+       OpcodeIQuery = 1
+       OpcodeStatus = 2
+       OpcodeNotify = 4
+       OpcodeUpdate = 5
+)
+
+// Header is the wire format for the DNS packet header.
+type Header struct {
+       Id                                 uint16
+       Bits                               uint16
+       Qdcount, Ancount, Nscount, Arcount uint16
+}
+
+const (
+       headerSize = 12
+
+       // Header.Bits
+       _QR = 1 << 15 // query/response (response=1)
+       _AA = 1 << 10 // authoritative
+       _TC = 1 << 9  // truncated
+       _RD = 1 << 8  // recursion desired
+       _RA = 1 << 7  // recursion available
+       _Z  = 1 << 6  // Z
+       _AD = 1 << 5  // authticated data
+       _CD = 1 << 4  // checking disabled
+)
+
+// Various constants used in the LOC RR, See RFC 1887.
+const (
+       LOC_EQUATOR       = 1 << 31 // RFC 1876, Section 2.
+       LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2.
+       LOC_HOURS         = 60 * 1000
+       LOC_DEGREES       = 60 * LOC_HOURS
+       LOC_ALTITUDEBASE  = 100000
+)
+
+// Different Certificate Types, see RFC 4398, Section 2.1
+const (
+       CertPKIX = 1 + iota
+       CertSPKI
+       CertPGP
+       CertIPIX
+       CertISPKI
+       CertIPGP
+       CertACPKIX
+       CertIACPKIX
+       CertURI = 253
+       CertOID = 254
+)
+
+// CertTypeToString converts the Cert Type to its string representation.
+// See RFC 4398 and RFC 6944.
+var CertTypeToString = map[uint16]string{
+       CertPKIX:    "PKIX",
+       CertSPKI:    "SPKI",
+       CertPGP:     "PGP",
+       CertIPIX:    "IPIX",
+       CertISPKI:   "ISPKI",
+       CertIPGP:    "IPGP",
+       CertACPKIX:  "ACPKIX",
+       CertIACPKIX: "IACPKIX",
+       CertURI:     "URI",
+       CertOID:     "OID",
+}
+
+//go:generate go run types_generate.go
+
+// Question holds a DNS question. There can be multiple questions in the
+// question section of a message. Usually there is just one.
+type Question struct {
+       Name   string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed)
+       Qtype  uint16
+       Qclass uint16
+}
+
+func (q *Question) len(off int, compression map[string]struct{}) int {
+       l := domainNameLen(q.Name, off, compression, true)
+       l += 2 + 2
+       return l
+}
+
+func (q *Question) String() (s string) {
+       // prefix with ; (as in dig)
+       s = ";" + sprintName(q.Name) + "\t"
+       s += Class(q.Qclass).String() + "\t"
+       s += " " + Type(q.Qtype).String()
+       return s
+}
+
+// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY
+// is named "*" there.
+type ANY struct {
+       Hdr RR_Header
+       // Does not have any rdata
+}
+
+func (rr *ANY) String() string { return rr.Hdr.String() }
+
+func (rr *ANY) parse(c *zlexer, origin, file string) *ParseError {
+       panic("dns: internal error: parse should never be called on ANY")
+}
+
+// NULL RR. See RFC 1035.
+type NULL struct {
+       Hdr  RR_Header
+       Data string `dns:"any"`
+}
+
+func (rr *NULL) String() string {
+       // There is no presentation format; prefix string with a comment.
+       return ";" + rr.Hdr.String() + rr.Data
+}
+
+func (rr *NULL) parse(c *zlexer, origin, file string) *ParseError {
+       panic("dns: internal error: parse should never be called on NULL")
+}
+
+// CNAME RR. See RFC 1034.
+type CNAME struct {
+       Hdr    RR_Header
+       Target string `dns:"cdomain-name"`
+}
+
+func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
+
+// HINFO RR. See RFC 1034.
+type HINFO struct {
+       Hdr RR_Header
+       Cpu string
+       Os  string
+}
+
+func (rr *HINFO) String() string {
+       return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os})
+}
+
+// MB RR. See RFC 1035.
+type MB struct {
+       Hdr RR_Header
+       Mb  string `dns:"cdomain-name"`
+}
+
+func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
+
+// MG RR. See RFC 1035.
+type MG struct {
+       Hdr RR_Header
+       Mg  string `dns:"cdomain-name"`
+}
+
+func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
+
+// MINFO RR. See RFC 1035.
+type MINFO struct {
+       Hdr   RR_Header
+       Rmail string `dns:"cdomain-name"`
+       Email string `dns:"cdomain-name"`
+}
+
+func (rr *MINFO) String() string {
+       return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email)
+}
+
+// MR RR. See RFC 1035.
+type MR struct {
+       Hdr RR_Header
+       Mr  string `dns:"cdomain-name"`
+}
+
+func (rr *MR) String() string {
+       return rr.Hdr.String() + sprintName(rr.Mr)
+}
+
+// MF RR. See RFC 1035.
+type MF struct {
+       Hdr RR_Header
+       Mf  string `dns:"cdomain-name"`
+}
+
+func (rr *MF) String() string {
+       return rr.Hdr.String() + sprintName(rr.Mf)
+}
+
+// MD RR. See RFC 1035.
+type MD struct {
+       Hdr RR_Header
+       Md  string `dns:"cdomain-name"`
+}
+
+func (rr *MD) String() string {
+       return rr.Hdr.String() + sprintName(rr.Md)
+}
+
+// MX RR. See RFC 1035.
+type MX struct {
+       Hdr        RR_Header
+       Preference uint16
+       Mx         string `dns:"cdomain-name"`
+}
+
+func (rr *MX) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx)
+}
+
+// AFSDB RR. See RFC 1183.
+type AFSDB struct {
+       Hdr      RR_Header
+       Subtype  uint16
+       Hostname string `dns:"domain-name"`
+}
+
+func (rr *AFSDB) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
+}
+
+// X25 RR. See RFC 1183, Section 3.1.
+type X25 struct {
+       Hdr         RR_Header
+       PSDNAddress string
+}
+
+func (rr *X25) String() string {
+       return rr.Hdr.String() + rr.PSDNAddress
+}
+
+// RT RR. See RFC 1183, Section 3.3.
+type RT struct {
+       Hdr        RR_Header
+       Preference uint16
+       Host       string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035.
+}
+
+func (rr *RT) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
+}
+
+// NS RR. See RFC 1035.
+type NS struct {
+       Hdr RR_Header
+       Ns  string `dns:"cdomain-name"`
+}
+
+func (rr *NS) String() string {
+       return rr.Hdr.String() + sprintName(rr.Ns)
+}
+
+// PTR RR. See RFC 1035.
+type PTR struct {
+       Hdr RR_Header
+       Ptr string `dns:"cdomain-name"`
+}
+
+func (rr *PTR) String() string {
+       return rr.Hdr.String() + sprintName(rr.Ptr)
+}
+
+// RP RR. See RFC 1138, Section 2.2.
+type RP struct {
+       Hdr  RR_Header
+       Mbox string `dns:"domain-name"`
+       Txt  string `dns:"domain-name"`
+}
+
+func (rr *RP) String() string {
+       return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt)
+}
+
+// SOA RR. See RFC 1035.
+type SOA struct {
+       Hdr     RR_Header
+       Ns      string `dns:"cdomain-name"`
+       Mbox    string `dns:"cdomain-name"`
+       Serial  uint32
+       Refresh uint32
+       Retry   uint32
+       Expire  uint32
+       Minttl  uint32
+}
+
+func (rr *SOA) String() string {
+       return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) +
+               " " + strconv.FormatInt(int64(rr.Serial), 10) +
+               " " + strconv.FormatInt(int64(rr.Refresh), 10) +
+               " " + strconv.FormatInt(int64(rr.Retry), 10) +
+               " " + strconv.FormatInt(int64(rr.Expire), 10) +
+               " " + strconv.FormatInt(int64(rr.Minttl), 10)
+}
+
+// TXT RR. See RFC 1035.
+type TXT struct {
+       Hdr RR_Header
+       Txt []string `dns:"txt"`
+}
+
+func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
+
+func sprintName(s string) string {
+       var dst bytes.Buffer
+       dst.Grow(len(s))
+       for i := 0; i < len(s); {
+               if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
+                       dst.WriteString(s[i : i+2])
+                       i += 2
+                       continue
+               }
+
+               b, n := nextByte(s, i)
+               switch {
+               case n == 0:
+                       i++ // dangling back slash
+               case b == '.':
+                       dst.WriteByte('.')
+               default:
+                       writeDomainNameByte(&dst, b)
+               }
+               i += n
+       }
+       return dst.String()
+}
+
+func sprintTxtOctet(s string) string {
+       var dst bytes.Buffer
+       dst.Grow(2 + len(s))
+       dst.WriteByte('"')
+       for i := 0; i < len(s); {
+               if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' {
+                       dst.WriteString(s[i : i+2])
+                       i += 2
+                       continue
+               }
+
+               b, n := nextByte(s, i)
+               switch {
+               case n == 0:
+                       i++ // dangling back slash
+               case b == '.':
+                       dst.WriteByte('.')
+               case b < ' ' || b > '~':
+                       dst.WriteString(escapeByte(b))
+               default:
+                       dst.WriteByte(b)
+               }
+               i += n
+       }
+       dst.WriteByte('"')
+       return dst.String()
+}
+
+func sprintTxt(txt []string) string {
+       var out bytes.Buffer
+       for i, s := range txt {
+               out.Grow(3 + len(s))
+               if i > 0 {
+                       out.WriteString(` "`)
+               } else {
+                       out.WriteByte('"')
+               }
+               for j := 0; j < len(s); {
+                       b, n := nextByte(s, j)
+                       if n == 0 {
+                               break
+                       }
+                       writeTXTStringByte(&out, b)
+                       j += n
+               }
+               out.WriteByte('"')
+       }
+       return out.String()
+}
+
+func writeDomainNameByte(s *bytes.Buffer, b byte) {
+       switch b {
+       case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
+               s.WriteByte('\\')
+               s.WriteByte(b)
+       default:
+               writeTXTStringByte(s, b)
+       }
+}
+
+func writeTXTStringByte(s *bytes.Buffer, b byte) {
+       switch {
+       case b == '"' || b == '\\':
+               s.WriteByte('\\')
+               s.WriteByte(b)
+       case b < ' ' || b > '~':
+               s.WriteString(escapeByte(b))
+       default:
+               s.WriteByte(b)
+       }
+}
+
+const (
+       escapedByteSmall = "" +
+               `\000\001\002\003\004\005\006\007\008\009` +
+               `\010\011\012\013\014\015\016\017\018\019` +
+               `\020\021\022\023\024\025\026\027\028\029` +
+               `\030\031`
+       escapedByteLarge = `\127\128\129` +
+               `\130\131\132\133\134\135\136\137\138\139` +
+               `\140\141\142\143\144\145\146\147\148\149` +
+               `\150\151\152\153\154\155\156\157\158\159` +
+               `\160\161\162\163\164\165\166\167\168\169` +
+               `\170\171\172\173\174\175\176\177\178\179` +
+               `\180\181\182\183\184\185\186\187\188\189` +
+               `\190\191\192\193\194\195\196\197\198\199` +
+               `\200\201\202\203\204\205\206\207\208\209` +
+               `\210\211\212\213\214\215\216\217\218\219` +
+               `\220\221\222\223\224\225\226\227\228\229` +
+               `\230\231\232\233\234\235\236\237\238\239` +
+               `\240\241\242\243\244\245\246\247\248\249` +
+               `\250\251\252\253\254\255`
+)
+
+// escapeByte returns the \DDD escaping of b which must
+// satisfy b < ' ' || b > '~'.
+func escapeByte(b byte) string {
+       if b < ' ' {
+               return escapedByteSmall[b*4 : b*4+4]
+       }
+
+       b -= '~' + 1
+       // The cast here is needed as b*4 may overflow byte.
+       return escapedByteLarge[int(b)*4 : int(b)*4+4]
+}
+
+func nextByte(s string, offset int) (byte, int) {
+       if offset >= len(s) {
+               return 0, 0
+       }
+       if s[offset] != '\\' {
+               // not an escape sequence
+               return s[offset], 1
+       }
+       switch len(s) - offset {
+       case 1: // dangling escape
+               return 0, 0
+       case 2, 3: // too short to be \ddd
+       default: // maybe \ddd
+               if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) {
+                       return dddStringToByte(s[offset+1:]), 4
+               }
+       }
+       // not \ddd, just an RFC 1035 "quoted" character
+       return s[offset+1], 2
+}
+
+// SPF RR. See RFC 4408, Section 3.1.1.
+type SPF struct {
+       Hdr RR_Header
+       Txt []string `dns:"txt"`
+}
+
+func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
+
+// AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template.
+type AVC struct {
+       Hdr RR_Header
+       Txt []string `dns:"txt"`
+}
+
+func (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
+
+// SRV RR. See RFC 2782.
+type SRV struct {
+       Hdr      RR_Header
+       Priority uint16
+       Weight   uint16
+       Port     uint16
+       Target   string `dns:"domain-name"`
+}
+
+func (rr *SRV) String() string {
+       return rr.Hdr.String() +
+               strconv.Itoa(int(rr.Priority)) + " " +
+               strconv.Itoa(int(rr.Weight)) + " " +
+               strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target)
+}
+
+// NAPTR RR. See RFC 2915.
+type NAPTR struct {
+       Hdr         RR_Header
+       Order       uint16
+       Preference  uint16
+       Flags       string
+       Service     string
+       Regexp      string
+       Replacement string `dns:"domain-name"`
+}
+
+func (rr *NAPTR) String() string {
+       return rr.Hdr.String() +
+               strconv.Itoa(int(rr.Order)) + " " +
+               strconv.Itoa(int(rr.Preference)) + " " +
+               "\"" + rr.Flags + "\" " +
+               "\"" + rr.Service + "\" " +
+               "\"" + rr.Regexp + "\" " +
+               rr.Replacement
+}
+
+// CERT RR. See RFC 4398.
+type CERT struct {
+       Hdr         RR_Header
+       Type        uint16
+       KeyTag      uint16
+       Algorithm   uint8
+       Certificate string `dns:"base64"`
+}
+
+func (rr *CERT) String() string {
+       var (
+               ok                  bool
+               certtype, algorithm string
+       )
+       if certtype, ok = CertTypeToString[rr.Type]; !ok {
+               certtype = strconv.Itoa(int(rr.Type))
+       }
+       if algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok {
+               algorithm = strconv.Itoa(int(rr.Algorithm))
+       }
+       return rr.Hdr.String() + certtype +
+               " " + strconv.Itoa(int(rr.KeyTag)) +
+               " " + algorithm +
+               " " + rr.Certificate
+}
+
+// DNAME RR. See RFC 2672.
+type DNAME struct {
+       Hdr    RR_Header
+       Target string `dns:"domain-name"`
+}
+
+func (rr *DNAME) String() string {
+       return rr.Hdr.String() + sprintName(rr.Target)
+}
+
+// A RR. See RFC 1035.
+type A struct {
+       Hdr RR_Header
+       A   net.IP `dns:"a"`
+}
+
+func (rr *A) String() string {
+       if rr.A == nil {
+               return rr.Hdr.String()
+       }
+       return rr.Hdr.String() + rr.A.String()
+}
+
+// AAAA RR. See RFC 3596.
+type AAAA struct {
+       Hdr  RR_Header
+       AAAA net.IP `dns:"aaaa"`
+}
+
+func (rr *AAAA) String() string {
+       if rr.AAAA == nil {
+               return rr.Hdr.String()
+       }
+       return rr.Hdr.String() + rr.AAAA.String()
+}
+
+// PX RR. See RFC 2163.
+type PX struct {
+       Hdr        RR_Header
+       Preference uint16
+       Map822     string `dns:"domain-name"`
+       Mapx400    string `dns:"domain-name"`
+}
+
+func (rr *PX) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400)
+}
+
+// GPOS RR. See RFC 1712.
+type GPOS struct {
+       Hdr       RR_Header
+       Longitude string
+       Latitude  string
+       Altitude  string
+}
+
+func (rr *GPOS) String() string {
+       return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude
+}
+
+// LOC RR. See RFC RFC 1876.
+type LOC struct {
+       Hdr       RR_Header
+       Version   uint8
+       Size      uint8
+       HorizPre  uint8
+       VertPre   uint8
+       Latitude  uint32
+       Longitude uint32
+       Altitude  uint32
+}
+
+// cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent
+// format and returns a string in m (two decimals for the cm)
+func cmToM(m, e uint8) string {
+       if e < 2 {
+               if e == 1 {
+                       m *= 10
+               }
+
+               return fmt.Sprintf("0.%02d", m)
+       }
+
+       s := fmt.Sprintf("%d", m)
+       for e > 2 {
+               s += "0"
+               e--
+       }
+       return s
+}
+
+func (rr *LOC) String() string {
+       s := rr.Hdr.String()
+
+       lat := rr.Latitude
+       ns := "N"
+       if lat > LOC_EQUATOR {
+               lat = lat - LOC_EQUATOR
+       } else {
+               ns = "S"
+               lat = LOC_EQUATOR - lat
+       }
+       h := lat / LOC_DEGREES
+       lat = lat % LOC_DEGREES
+       m := lat / LOC_HOURS
+       lat = lat % LOC_HOURS
+       s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns)
+
+       lon := rr.Longitude
+       ew := "E"
+       if lon > LOC_PRIMEMERIDIAN {
+               lon = lon - LOC_PRIMEMERIDIAN
+       } else {
+               ew = "W"
+               lon = LOC_PRIMEMERIDIAN - lon
+       }
+       h = lon / LOC_DEGREES
+       lon = lon % LOC_DEGREES
+       m = lon / LOC_HOURS
+       lon = lon % LOC_HOURS
+       s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew)
+
+       var alt = float64(rr.Altitude) / 100
+       alt -= LOC_ALTITUDEBASE
+       if rr.Altitude%100 != 0 {
+               s += fmt.Sprintf("%.2fm ", alt)
+       } else {
+               s += fmt.Sprintf("%.0fm ", alt)
+       }
+
+       s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m "
+       s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m "
+       s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m"
+
+       return s
+}
+
+// SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931.
+type SIG struct {
+       RRSIG
+}
+
+// RRSIG RR. See RFC 4034 and RFC 3755.
+type RRSIG struct {
+       Hdr         RR_Header
+       TypeCovered uint16
+       Algorithm   uint8
+       Labels      uint8
+       OrigTtl     uint32
+       Expiration  uint32
+       Inception   uint32
+       KeyTag      uint16
+       SignerName  string `dns:"domain-name"`
+       Signature   string `dns:"base64"`
+}
+
+func (rr *RRSIG) String() string {
+       s := rr.Hdr.String()
+       s += Type(rr.TypeCovered).String()
+       s += " " + strconv.Itoa(int(rr.Algorithm)) +
+               " " + strconv.Itoa(int(rr.Labels)) +
+               " " + strconv.FormatInt(int64(rr.OrigTtl), 10) +
+               " " + TimeToString(rr.Expiration) +
+               " " + TimeToString(rr.Inception) +
+               " " + strconv.Itoa(int(rr.KeyTag)) +
+               " " + sprintName(rr.SignerName) +
+               " " + rr.Signature
+       return s
+}
+
+// NSEC RR. See RFC 4034 and RFC 3755.
+type NSEC struct {
+       Hdr        RR_Header
+       NextDomain string   `dns:"domain-name"`
+       TypeBitMap []uint16 `dns:"nsec"`
+}
+
+func (rr *NSEC) String() string {
+       s := rr.Hdr.String() + sprintName(rr.NextDomain)
+       for i := 0; i < len(rr.TypeBitMap); i++ {
+               s += " " + Type(rr.TypeBitMap[i]).String()
+       }
+       return s
+}
+
+func (rr *NSEC) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.NextDomain, off+l, compression, false)
+       lastwindow := uint32(2 ^ 32 + 1)
+       for _, t := range rr.TypeBitMap {
+               window := t / 256
+               if uint32(window) != lastwindow {
+                       l += 1 + 32
+               }
+               lastwindow = uint32(window)
+       }
+       return l
+}
+
+// DLV RR. See RFC 4431.
+type DLV struct{ DS }
+
+// CDS RR. See RFC 7344.
+type CDS struct{ DS }
+
+// DS RR. See RFC 4034 and RFC 3658.
+type DS struct {
+       Hdr        RR_Header
+       KeyTag     uint16
+       Algorithm  uint8
+       DigestType uint8
+       Digest     string `dns:"hex"`
+}
+
+func (rr *DS) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
+               " " + strconv.Itoa(int(rr.Algorithm)) +
+               " " + strconv.Itoa(int(rr.DigestType)) +
+               " " + strings.ToUpper(rr.Digest)
+}
+
+// KX RR. See RFC 2230.
+type KX struct {
+       Hdr        RR_Header
+       Preference uint16
+       Exchanger  string `dns:"domain-name"`
+}
+
+func (rr *KX) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
+               " " + sprintName(rr.Exchanger)
+}
+
+// TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf.
+type TA struct {
+       Hdr        RR_Header
+       KeyTag     uint16
+       Algorithm  uint8
+       DigestType uint8
+       Digest     string `dns:"hex"`
+}
+
+func (rr *TA) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
+               " " + strconv.Itoa(int(rr.Algorithm)) +
+               " " + strconv.Itoa(int(rr.DigestType)) +
+               " " + strings.ToUpper(rr.Digest)
+}
+
+// TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template.
+type TALINK struct {
+       Hdr          RR_Header
+       PreviousName string `dns:"domain-name"`
+       NextName     string `dns:"domain-name"`
+}
+
+func (rr *TALINK) String() string {
+       return rr.Hdr.String() +
+               sprintName(rr.PreviousName) + " " + sprintName(rr.NextName)
+}
+
+// SSHFP RR. See RFC RFC 4255.
+type SSHFP struct {
+       Hdr         RR_Header
+       Algorithm   uint8
+       Type        uint8
+       FingerPrint string `dns:"hex"`
+}
+
+func (rr *SSHFP) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) +
+               " " + strconv.Itoa(int(rr.Type)) +
+               " " + strings.ToUpper(rr.FingerPrint)
+}
+
+// KEY RR. See RFC RFC 2535.
+type KEY struct {
+       DNSKEY
+}
+
+// CDNSKEY RR. See RFC 7344.
+type CDNSKEY struct {
+       DNSKEY
+}
+
+// DNSKEY RR. See RFC 4034 and RFC 3755.
+type DNSKEY struct {
+       Hdr       RR_Header
+       Flags     uint16
+       Protocol  uint8
+       Algorithm uint8
+       PublicKey string `dns:"base64"`
+}
+
+func (rr *DNSKEY) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
+               " " + strconv.Itoa(int(rr.Protocol)) +
+               " " + strconv.Itoa(int(rr.Algorithm)) +
+               " " + rr.PublicKey
+}
+
+// RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template.
+type RKEY struct {
+       Hdr       RR_Header
+       Flags     uint16
+       Protocol  uint8
+       Algorithm uint8
+       PublicKey string `dns:"base64"`
+}
+
+func (rr *RKEY) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
+               " " + strconv.Itoa(int(rr.Protocol)) +
+               " " + strconv.Itoa(int(rr.Algorithm)) +
+               " " + rr.PublicKey
+}
+
+// NSAPPTR RR. See RFC 1348.
+type NSAPPTR struct {
+       Hdr RR_Header
+       Ptr string `dns:"domain-name"`
+}
+
+func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
+
+// NSEC3 RR. See RFC 5155.
+type NSEC3 struct {
+       Hdr        RR_Header
+       Hash       uint8
+       Flags      uint8
+       Iterations uint16
+       SaltLength uint8
+       Salt       string `dns:"size-hex:SaltLength"`
+       HashLength uint8
+       NextDomain string   `dns:"size-base32:HashLength"`
+       TypeBitMap []uint16 `dns:"nsec"`
+}
+
+func (rr *NSEC3) String() string {
+       s := rr.Hdr.String()
+       s += strconv.Itoa(int(rr.Hash)) +
+               " " + strconv.Itoa(int(rr.Flags)) +
+               " " + strconv.Itoa(int(rr.Iterations)) +
+               " " + saltToString(rr.Salt) +
+               " " + rr.NextDomain
+       for i := 0; i < len(rr.TypeBitMap); i++ {
+               s += " " + Type(rr.TypeBitMap[i]).String()
+       }
+       return s
+}
+
+func (rr *NSEC3) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1
+       lastwindow := uint32(2 ^ 32 + 1)
+       for _, t := range rr.TypeBitMap {
+               window := t / 256
+               if uint32(window) != lastwindow {
+                       l += 1 + 32
+               }
+               lastwindow = uint32(window)
+       }
+       return l
+}
+
+// NSEC3PARAM RR. See RFC 5155.
+type NSEC3PARAM struct {
+       Hdr        RR_Header
+       Hash       uint8
+       Flags      uint8
+       Iterations uint16
+       SaltLength uint8
+       Salt       string `dns:"size-hex:SaltLength"`
+}
+
+func (rr *NSEC3PARAM) String() string {
+       s := rr.Hdr.String()
+       s += strconv.Itoa(int(rr.Hash)) +
+               " " + strconv.Itoa(int(rr.Flags)) +
+               " " + strconv.Itoa(int(rr.Iterations)) +
+               " " + saltToString(rr.Salt)
+       return s
+}
+
+// TKEY RR. See RFC 2930.
+type TKEY struct {
+       Hdr        RR_Header
+       Algorithm  string `dns:"domain-name"`
+       Inception  uint32
+       Expiration uint32
+       Mode       uint16
+       Error      uint16
+       KeySize    uint16
+       Key        string `dns:"size-hex:KeySize"`
+       OtherLen   uint16
+       OtherData  string `dns:"size-hex:OtherLen"`
+}
+
+// TKEY has no official presentation format, but this will suffice.
+func (rr *TKEY) String() string {
+       s := ";" + rr.Hdr.String() +
+               " " + rr.Algorithm +
+               " " + TimeToString(rr.Inception) +
+               " " + TimeToString(rr.Expiration) +
+               " " + strconv.Itoa(int(rr.Mode)) +
+               " " + strconv.Itoa(int(rr.Error)) +
+               " " + strconv.Itoa(int(rr.KeySize)) +
+               " " + rr.Key +
+               " " + strconv.Itoa(int(rr.OtherLen)) +
+               " " + rr.OtherData
+       return s
+}
+
+// RFC3597 represents an unknown/generic RR. See RFC 3597.
+type RFC3597 struct {
+       Hdr   RR_Header
+       Rdata string `dns:"hex"`
+}
+
+func (rr *RFC3597) String() string {
+       // Let's call it a hack
+       s := rfc3597Header(rr.Hdr)
+
+       s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata
+       return s
+}
+
+func rfc3597Header(h RR_Header) string {
+       var s string
+
+       s += sprintName(h.Name) + "\t"
+       s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
+       s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
+       s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
+       return s
+}
+
+// URI RR. See RFC 7553.
+type URI struct {
+       Hdr      RR_Header
+       Priority uint16
+       Weight   uint16
+       Target   string `dns:"octet"`
+}
+
+func (rr *URI) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
+               " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
+}
+
+// DHCID RR. See RFC 4701.
+type DHCID struct {
+       Hdr    RR_Header
+       Digest string `dns:"base64"`
+}
+
+func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
+
+// TLSA RR. See RFC 6698.
+type TLSA struct {
+       Hdr          RR_Header
+       Usage        uint8
+       Selector     uint8
+       MatchingType uint8
+       Certificate  string `dns:"hex"`
+}
+
+func (rr *TLSA) String() string {
+       return rr.Hdr.String() +
+               strconv.Itoa(int(rr.Usage)) +
+               " " + strconv.Itoa(int(rr.Selector)) +
+               " " + strconv.Itoa(int(rr.MatchingType)) +
+               " " + rr.Certificate
+}
+
+// SMIMEA RR. See RFC 8162.
+type SMIMEA struct {
+       Hdr          RR_Header
+       Usage        uint8
+       Selector     uint8
+       MatchingType uint8
+       Certificate  string `dns:"hex"`
+}
+
+func (rr *SMIMEA) String() string {
+       s := rr.Hdr.String() +
+               strconv.Itoa(int(rr.Usage)) +
+               " " + strconv.Itoa(int(rr.Selector)) +
+               " " + strconv.Itoa(int(rr.MatchingType))
+
+       // Every Nth char needs a space on this output. If we output
+       // this as one giant line, we can't read it can in because in some cases
+       // the cert length overflows scan.maxTok (2048).
+       sx := splitN(rr.Certificate, 1024) // conservative value here
+       s += " " + strings.Join(sx, " ")
+       return s
+}
+
+// HIP RR. See RFC 8005.
+type HIP struct {
+       Hdr                RR_Header
+       HitLength          uint8
+       PublicKeyAlgorithm uint8
+       PublicKeyLength    uint16
+       Hit                string   `dns:"size-hex:HitLength"`
+       PublicKey          string   `dns:"size-base64:PublicKeyLength"`
+       RendezvousServers  []string `dns:"domain-name"`
+}
+
+func (rr *HIP) String() string {
+       s := rr.Hdr.String() +
+               strconv.Itoa(int(rr.PublicKeyAlgorithm)) +
+               " " + rr.Hit +
+               " " + rr.PublicKey
+       for _, d := range rr.RendezvousServers {
+               s += " " + sprintName(d)
+       }
+       return s
+}
+
+// NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template.
+type NINFO struct {
+       Hdr    RR_Header
+       ZSData []string `dns:"txt"`
+}
+
+func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
+
+// NID RR. See RFC RFC 6742.
+type NID struct {
+       Hdr        RR_Header
+       Preference uint16
+       NodeID     uint64
+}
+
+func (rr *NID) String() string {
+       s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
+       node := fmt.Sprintf("%0.16x", rr.NodeID)
+       s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
+       return s
+}
+
+// L32 RR, See RFC 6742.
+type L32 struct {
+       Hdr        RR_Header
+       Preference uint16
+       Locator32  net.IP `dns:"a"`
+}
+
+func (rr *L32) String() string {
+       if rr.Locator32 == nil {
+               return rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
+       }
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
+               " " + rr.Locator32.String()
+}
+
+// L64 RR, See RFC 6742.
+type L64 struct {
+       Hdr        RR_Header
+       Preference uint16
+       Locator64  uint64
+}
+
+func (rr *L64) String() string {
+       s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
+       node := fmt.Sprintf("%0.16X", rr.Locator64)
+       s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16]
+       return s
+}
+
+// LP RR. See RFC 6742.
+type LP struct {
+       Hdr        RR_Header
+       Preference uint16
+       Fqdn       string `dns:"domain-name"`
+}
+
+func (rr *LP) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn)
+}
+
+// EUI48 RR. See RFC 7043.
+type EUI48 struct {
+       Hdr     RR_Header
+       Address uint64 `dns:"uint48"`
+}
+
+func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
+
+// EUI64 RR. See RFC 7043.
+type EUI64 struct {
+       Hdr     RR_Header
+       Address uint64
+}
+
+func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
+
+// CAA RR. See RFC 6844.
+type CAA struct {
+       Hdr   RR_Header
+       Flag  uint8
+       Tag   string
+       Value string `dns:"octet"`
+}
+
+func (rr *CAA) String() string {
+       return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
+}
+
+// UID RR. Deprecated, IANA-Reserved.
+type UID struct {
+       Hdr RR_Header
+       Uid uint32
+}
+
+func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
+
+// GID RR. Deprecated, IANA-Reserved.
+type GID struct {
+       Hdr RR_Header
+       Gid uint32
+}
+
+func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
+
+// UINFO RR. Deprecated, IANA-Reserved.
+type UINFO struct {
+       Hdr   RR_Header
+       Uinfo string
+}
+
+func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
+
+// EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.
+type EID struct {
+       Hdr      RR_Header
+       Endpoint string `dns:"hex"`
+}
+
+func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
+
+// NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt.
+type NIMLOC struct {
+       Hdr     RR_Header
+       Locator string `dns:"hex"`
+}
+
+func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
+
+// OPENPGPKEY RR. See RFC 7929.
+type OPENPGPKEY struct {
+       Hdr       RR_Header
+       PublicKey string `dns:"base64"`
+}
+
+func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
+
+// CSYNC RR. See RFC 7477.
+type CSYNC struct {
+       Hdr        RR_Header
+       Serial     uint32
+       Flags      uint16
+       TypeBitMap []uint16 `dns:"nsec"`
+}
+
+func (rr *CSYNC) String() string {
+       s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags))
+
+       for i := 0; i < len(rr.TypeBitMap); i++ {
+               s += " " + Type(rr.TypeBitMap[i]).String()
+       }
+       return s
+}
+
+func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 4 + 2
+       lastwindow := uint32(2 ^ 32 + 1)
+       for _, t := range rr.TypeBitMap {
+               window := t / 256
+               if uint32(window) != lastwindow {
+                       l += 1 + 32
+               }
+               lastwindow = uint32(window)
+       }
+       return l
+}
+
+// TimeToString translates the RRSIG's incep. and expir. times to the
+// string representation used when printing the record.
+// It takes serial arithmetic (RFC 1982) into account.
+func TimeToString(t uint32) string {
+       mod := (int64(t)-time.Now().Unix())/year68 - 1
+       if mod < 0 {
+               mod = 0
+       }
+       ti := time.Unix(int64(t)-mod*year68, 0).UTC()
+       return ti.Format("20060102150405")
+}
+
+// StringToTime translates the RRSIG's incep. and expir. times from
+// string values like "20110403154150" to an 32 bit integer.
+// It takes serial arithmetic (RFC 1982) into account.
+func StringToTime(s string) (uint32, error) {
+       t, err := time.Parse("20060102150405", s)
+       if err != nil {
+               return 0, err
+       }
+       mod := t.Unix()/year68 - 1
+       if mod < 0 {
+               mod = 0
+       }
+       return uint32(t.Unix() - mod*year68), nil
+}
+
+// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
+func saltToString(s string) string {
+       if len(s) == 0 {
+               return "-"
+       }
+       return strings.ToUpper(s)
+}
+
+func euiToString(eui uint64, bits int) (hex string) {
+       switch bits {
+       case 64:
+               hex = fmt.Sprintf("%16.16x", eui)
+               hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
+                       "-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16]
+       case 48:
+               hex = fmt.Sprintf("%12.12x", eui)
+               hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] +
+                       "-" + hex[8:10] + "-" + hex[10:12]
+       }
+       return
+}
+
+// copyIP returns a copy of ip.
+func copyIP(ip net.IP) net.IP {
+       p := make(net.IP, len(ip))
+       copy(p, ip)
+       return p
+}
+
+// SplitN splits a string into N sized string chunks.
+// This might become an exported function once.
+func splitN(s string, n int) []string {
+       if len(s) < n {
+               return []string{s}
+       }
+       sx := []string{}
+       p, i := 0, n
+       for {
+               if i <= len(s) {
+                       sx = append(sx, s[p:i])
+               } else {
+                       sx = append(sx, s[p:])
+                       break
+
+               }
+               p, i = p+n, i+n
+       }
+
+       return sx
+}
diff --git a/vendor/github.com/miekg/dns/types_generate.go b/vendor/github.com/miekg/dns/types_generate.go
new file mode 100644 (file)
index 0000000..aa05a08
--- /dev/null
@@ -0,0 +1,287 @@
+//+build ignore
+
+// types_generate.go is meant to run with go generate. It will use
+// go/{importer,types} to track down all the RR struct types. Then for each type
+// it will generate conversion tables (TypeToRR and TypeToString) and banal
+// methods (len, Header, copy) based on the struct tags. The generated source is
+// written to ztypes.go, and is meant to be checked into git.
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "go/format"
+       "go/importer"
+       "go/types"
+       "log"
+       "os"
+       "strings"
+       "text/template"
+)
+
+var skipLen = map[string]struct{}{
+       "NSEC":  {},
+       "NSEC3": {},
+       "OPT":   {},
+       "CSYNC": {},
+}
+
+var packageHdr = `
+// Code generated by "go run types_generate.go"; DO NOT EDIT.
+
+package dns
+
+import (
+       "encoding/base64"
+       "net"
+)
+
+`
+
+var TypeToRR = template.Must(template.New("TypeToRR").Parse(`
+// TypeToRR is a map of constructors for each RR type.
+var TypeToRR = map[uint16]func() RR{
+{{range .}}{{if ne . "RFC3597"}}  Type{{.}}:  func() RR { return new({{.}}) },
+{{end}}{{end}}                    }
+
+`))
+
+var typeToString = template.Must(template.New("typeToString").Parse(`
+// TypeToString is a map of strings for each RR type.
+var TypeToString = map[uint16]string{
+{{range .}}{{if ne . "NSAPPTR"}}  Type{{.}}: "{{.}}",
+{{end}}{{end}}                    TypeNSAPPTR:    "NSAP-PTR",
+}
+
+`))
+
+var headerFunc = template.Must(template.New("headerFunc").Parse(`
+{{range .}}  func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr }
+{{end}}
+
+`))
+
+// getTypeStruct will take a type and the package scope, and return the
+// (innermost) struct if the type is considered a RR type (currently defined as
+// those structs beginning with a RR_Header, could be redefined as implementing
+// the RR interface). The bool return value indicates if embedded structs were
+// resolved.
+func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
+       st, ok := t.Underlying().(*types.Struct)
+       if !ok {
+               return nil, false
+       }
+       if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
+               return st, false
+       }
+       if st.Field(0).Anonymous() {
+               st, _ := getTypeStruct(st.Field(0).Type(), scope)
+               return st, true
+       }
+       return nil, false
+}
+
+func main() {
+       // Import and type-check the package
+       pkg, err := importer.Default().Import("github.com/miekg/dns")
+       fatalIfErr(err)
+       scope := pkg.Scope()
+
+       // Collect constants like TypeX
+       var numberedTypes []string
+       for _, name := range scope.Names() {
+               o := scope.Lookup(name)
+               if o == nil || !o.Exported() {
+                       continue
+               }
+               b, ok := o.Type().(*types.Basic)
+               if !ok || b.Kind() != types.Uint16 {
+                       continue
+               }
+               if !strings.HasPrefix(o.Name(), "Type") {
+                       continue
+               }
+               name := strings.TrimPrefix(o.Name(), "Type")
+               if name == "PrivateRR" {
+                       continue
+               }
+               numberedTypes = append(numberedTypes, name)
+       }
+
+       // Collect actual types (*X)
+       var namedTypes []string
+       for _, name := range scope.Names() {
+               o := scope.Lookup(name)
+               if o == nil || !o.Exported() {
+                       continue
+               }
+               if st, _ := getTypeStruct(o.Type(), scope); st == nil {
+                       continue
+               }
+               if name == "PrivateRR" {
+                       continue
+               }
+
+               // Check if corresponding TypeX exists
+               if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
+                       log.Fatalf("Constant Type%s does not exist.", o.Name())
+               }
+
+               namedTypes = append(namedTypes, o.Name())
+       }
+
+       b := &bytes.Buffer{}
+       b.WriteString(packageHdr)
+
+       // Generate TypeToRR
+       fatalIfErr(TypeToRR.Execute(b, namedTypes))
+
+       // Generate typeToString
+       fatalIfErr(typeToString.Execute(b, numberedTypes))
+
+       // Generate headerFunc
+       fatalIfErr(headerFunc.Execute(b, namedTypes))
+
+       // Generate len()
+       fmt.Fprint(b, "// len() functions\n")
+       for _, name := range namedTypes {
+               if _, ok := skipLen[name]; ok {
+                       continue
+               }
+               o := scope.Lookup(name)
+               st, isEmbedded := getTypeStruct(o.Type(), scope)
+               if isEmbedded {
+                       continue
+               }
+               fmt.Fprintf(b, "func (rr *%s) len(off int, compression map[string]struct{}) int {\n", name)
+               fmt.Fprintf(b, "l := rr.Hdr.len(off, compression)\n")
+               for i := 1; i < st.NumFields(); i++ {
+                       o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
+
+                       if _, ok := st.Field(i).Type().(*types.Slice); ok {
+                               switch st.Tag(i) {
+                               case `dns:"-"`:
+                                       // ignored
+                               case `dns:"cdomain-name"`:
+                                       o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, true) }\n")
+                               case `dns:"domain-name"`:
+                                       o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\n")
+                               case `dns:"txt"`:
+                                       o("for _, x := range rr.%s { l += len(x) + 1 }\n")
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                               }
+                               continue
+                       }
+
+                       switch {
+                       case st.Tag(i) == `dns:"-"`:
+                               // ignored
+                       case st.Tag(i) == `dns:"cdomain-name"`:
+                               o("l += domainNameLen(rr.%s, off+l, compression, true)\n")
+                       case st.Tag(i) == `dns:"domain-name"`:
+                               o("l += domainNameLen(rr.%s, off+l, compression, false)\n")
+                       case st.Tag(i) == `dns:"octet"`:
+                               o("l += len(rr.%s)\n")
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-base64`):
+                               fallthrough
+                       case st.Tag(i) == `dns:"base64"`:
+                               o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-hex:`): // this has an extra field where the length is stored
+                               o("l += len(rr.%s)/2\n")
+                       case strings.HasPrefix(st.Tag(i), `dns:"size-hex`):
+                               fallthrough
+                       case st.Tag(i) == `dns:"hex"`:
+                               o("l += len(rr.%s)/2 + 1\n")
+                       case st.Tag(i) == `dns:"any"`:
+                               o("l += len(rr.%s)\n")
+                       case st.Tag(i) == `dns:"a"`:
+                               o("l += net.IPv4len // %s\n")
+                       case st.Tag(i) == `dns:"aaaa"`:
+                               o("l += net.IPv6len // %s\n")
+                       case st.Tag(i) == `dns:"txt"`:
+                               o("for _, t := range rr.%s { l += len(t) + 1 }\n")
+                       case st.Tag(i) == `dns:"uint48"`:
+                               o("l += 6 // %s\n")
+                       case st.Tag(i) == "":
+                               switch st.Field(i).Type().(*types.Basic).Kind() {
+                               case types.Uint8:
+                                       o("l++ // %s\n")
+                               case types.Uint16:
+                                       o("l += 2 // %s\n")
+                               case types.Uint32:
+                                       o("l += 4 // %s\n")
+                               case types.Uint64:
+                                       o("l += 8 // %s\n")
+                               case types.String:
+                                       o("l += len(rr.%s) + 1\n")
+                               default:
+                                       log.Fatalln(name, st.Field(i).Name())
+                               }
+                       default:
+                               log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
+                       }
+               }
+               fmt.Fprintf(b, "return l }\n")
+       }
+
+       // Generate copy()
+       fmt.Fprint(b, "// copy() functions\n")
+       for _, name := range namedTypes {
+               o := scope.Lookup(name)
+               st, isEmbedded := getTypeStruct(o.Type(), scope)
+               if isEmbedded {
+                       continue
+               }
+               fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
+               fields := []string{"rr.Hdr"}
+               for i := 1; i < st.NumFields(); i++ {
+                       f := st.Field(i).Name()
+                       if sl, ok := st.Field(i).Type().(*types.Slice); ok {
+                               t := sl.Underlying().String()
+                               t = strings.TrimPrefix(t, "[]")
+                               if strings.Contains(t, ".") {
+                                       splits := strings.Split(t, ".")
+                                       t = splits[len(splits)-1]
+                               }
+                               // For the EDNS0 interface (used in the OPT RR), we need to call the copy method on each element.
+                               if t == "EDNS0" {
+                                       fmt.Fprintf(b, "%s := make([]%s, len(rr.%s));\nfor i,e := range rr.%s {\n %s[i] = e.copy()\n}\n",
+                                               f, t, f, f, f)
+                                       fields = append(fields, f)
+                                       continue
+                               }
+                               fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
+                                       f, t, f, f, f)
+                               fields = append(fields, f)
+                               continue
+                       }
+                       if st.Field(i).Type().String() == "net.IP" {
+                               fields = append(fields, "copyIP(rr."+f+")")
+                               continue
+                       }
+                       fields = append(fields, "rr."+f)
+               }
+               fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ","))
+               fmt.Fprintf(b, "}\n")
+       }
+
+       // gofmt
+       res, err := format.Source(b.Bytes())
+       if err != nil {
+               b.WriteTo(os.Stderr)
+               log.Fatal(err)
+       }
+
+       // write result
+       f, err := os.Create("ztypes.go")
+       fatalIfErr(err)
+       defer f.Close()
+       f.Write(res)
+}
+
+func fatalIfErr(err error) {
+       if err != nil {
+               log.Fatal(err)
+       }
+}
diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go
new file mode 100644 (file)
index 0000000..a4826ee
--- /dev/null
@@ -0,0 +1,102 @@
+// +build !windows
+
+package dns
+
+import (
+       "net"
+
+       "golang.org/x/net/ipv4"
+       "golang.org/x/net/ipv6"
+)
+
+// This is the required size of the OOB buffer to pass to ReadMsgUDP.
+var udpOOBSize = func() int {
+       // We can't know whether we'll get an IPv4 control message or an
+       // IPv6 control message ahead of time. To get around this, we size
+       // the buffer equal to the largest of the two.
+
+       oob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface)
+       oob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface)
+
+       if len(oob4) > len(oob6) {
+               return len(oob4)
+       }
+
+       return len(oob6)
+}()
+
+// SessionUDP holds the remote address and the associated
+// out-of-band data.
+type SessionUDP struct {
+       raddr   *net.UDPAddr
+       context []byte
+}
+
+// RemoteAddr returns the remote network address.
+func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
+
+// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
+// net.UDPAddr.
+func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
+       oob := make([]byte, udpOOBSize)
+       n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
+       if err != nil {
+               return n, nil, err
+       }
+       return n, &SessionUDP{raddr, oob[:oobn]}, err
+}
+
+// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
+func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
+       oob := correctSource(session.context)
+       n, _, err := conn.WriteMsgUDP(b, oob, session.raddr)
+       return n, err
+}
+
+func setUDPSocketOptions(conn *net.UDPConn) error {
+       // Try setting the flags for both families and ignore the errors unless they
+       // both error.
+       err6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)
+       err4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
+       if err6 != nil && err4 != nil {
+               return err4
+       }
+       return nil
+}
+
+// parseDstFromOOB takes oob data and returns the destination IP.
+func parseDstFromOOB(oob []byte) net.IP {
+       // Start with IPv6 and then fallback to IPv4
+       // TODO(fastest963): Figure out a way to prefer one or the other. Looking at
+       // the lvl of the header for a 0 or 41 isn't cross-platform.
+       cm6 := new(ipv6.ControlMessage)
+       if cm6.Parse(oob) == nil && cm6.Dst != nil {
+               return cm6.Dst
+       }
+       cm4 := new(ipv4.ControlMessage)
+       if cm4.Parse(oob) == nil && cm4.Dst != nil {
+               return cm4.Dst
+       }
+       return nil
+}
+
+// correctSource takes oob data and returns new oob data with the Src equal to the Dst
+func correctSource(oob []byte) []byte {
+       dst := parseDstFromOOB(oob)
+       if dst == nil {
+               return nil
+       }
+       // If the dst is definitely an IPv6, then use ipv6's ControlMessage to
+       // respond otherwise use ipv4's because ipv6's marshal ignores ipv4
+       // addresses.
+       if dst.To4() == nil {
+               cm := new(ipv6.ControlMessage)
+               cm.Src = dst
+               oob = cm.Marshal()
+       } else {
+               cm := new(ipv4.ControlMessage)
+               cm.Src = dst
+               oob = cm.Marshal()
+       }
+       return oob
+}
diff --git a/vendor/github.com/miekg/dns/udp_windows.go b/vendor/github.com/miekg/dns/udp_windows.go
new file mode 100644 (file)
index 0000000..e7dd8ca
--- /dev/null
@@ -0,0 +1,35 @@
+// +build windows
+
+package dns
+
+import "net"
+
+// SessionUDP holds the remote address
+type SessionUDP struct {
+       raddr *net.UDPAddr
+}
+
+// RemoteAddr returns the remote network address.
+func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
+
+// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
+// net.UDPAddr.
+// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP.
+func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
+       n, raddr, err := conn.ReadFrom(b)
+       if err != nil {
+               return n, nil, err
+       }
+       return n, &SessionUDP{raddr.(*net.UDPAddr)}, err
+}
+
+// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr.
+// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP.
+func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
+       return conn.WriteTo(b, session.raddr)
+}
+
+// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods
+// use the standard method in udp.go for these.
+func setUDPSocketOptions(*net.UDPConn) error { return nil }
+func parseDstFromOOB([]byte, net.IP) net.IP  { return nil }
diff --git a/vendor/github.com/miekg/dns/update.go b/vendor/github.com/miekg/dns/update.go
new file mode 100644 (file)
index 0000000..69dd386
--- /dev/null
@@ -0,0 +1,110 @@
+package dns
+
+// NameUsed sets the RRs in the prereq section to
+// "Name is in use" RRs. RFC 2136 section 2.4.4.
+func (u *Msg) NameUsed(rr []RR) {
+       if u.Answer == nil {
+               u.Answer = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
+       }
+}
+
+// NameNotUsed sets the RRs in the prereq section to
+// "Name is in not use" RRs. RFC 2136 section 2.4.5.
+func (u *Msg) NameNotUsed(rr []RR) {
+       if u.Answer == nil {
+               u.Answer = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}})
+       }
+}
+
+// Used sets the RRs in the prereq section to
+// "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2.
+func (u *Msg) Used(rr []RR) {
+       if len(u.Question) == 0 {
+               panic("dns: empty question section")
+       }
+       if u.Answer == nil {
+               u.Answer = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               r.Header().Class = u.Question[0].Qclass
+               u.Answer = append(u.Answer, r)
+       }
+}
+
+// RRsetUsed sets the RRs in the prereq section to
+// "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1.
+func (u *Msg) RRsetUsed(rr []RR) {
+       if u.Answer == nil {
+               u.Answer = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               h := r.Header()
+               u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}})
+       }
+}
+
+// RRsetNotUsed sets the RRs in the prereq section to
+// "RRset does not exist" RRs. RFC 2136 section 2.4.3.
+func (u *Msg) RRsetNotUsed(rr []RR) {
+       if u.Answer == nil {
+               u.Answer = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               h := r.Header()
+               u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassNONE}})
+       }
+}
+
+// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1.
+func (u *Msg) Insert(rr []RR) {
+       if len(u.Question) == 0 {
+               panic("dns: empty question section")
+       }
+       if u.Ns == nil {
+               u.Ns = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               r.Header().Class = u.Question[0].Qclass
+               u.Ns = append(u.Ns, r)
+       }
+}
+
+// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.
+func (u *Msg) RemoveRRset(rr []RR) {
+       if u.Ns == nil {
+               u.Ns = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               h := r.Header()
+               u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}})
+       }
+}
+
+// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3
+func (u *Msg) RemoveName(rr []RR) {
+       if u.Ns == nil {
+               u.Ns = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}})
+       }
+}
+
+// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4
+func (u *Msg) Remove(rr []RR) {
+       if u.Ns == nil {
+               u.Ns = make([]RR, 0, len(rr))
+       }
+       for _, r := range rr {
+               h := r.Header()
+               h.Class = ClassNONE
+               h.Ttl = 0
+               u.Ns = append(u.Ns, r)
+       }
+}
diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go
new file mode 100644 (file)
index 0000000..46d644c
--- /dev/null
@@ -0,0 +1,15 @@
+package dns
+
+import "fmt"
+
+// Version is current version of this library.
+var Version = V{1, 1, 4}
+
+// V holds the version of this library.
+type V struct {
+       Major, Minor, Patch int
+}
+
+func (v V) String() string {
+       return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
+}
diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go
new file mode 100644 (file)
index 0000000..82afc52
--- /dev/null
@@ -0,0 +1,260 @@
+package dns
+
+import (
+       "fmt"
+       "time"
+)
+
+// Envelope is used when doing a zone transfer with a remote server.
+type Envelope struct {
+       RR    []RR  // The set of RRs in the answer section of the xfr reply message.
+       Error error // If something went wrong, this contains the error.
+}
+
+// A Transfer defines parameters that are used during a zone transfer.
+type Transfer struct {
+       *Conn
+       DialTimeout    time.Duration     // net.DialTimeout, defaults to 2 seconds
+       ReadTimeout    time.Duration     // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
+       WriteTimeout   time.Duration     // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
+       TsigSecret     map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
+       tsigTimersOnly bool
+}
+
+// Think we need to away to stop the transfer
+
+// In performs an incoming transfer with the server in a.
+// If you would like to set the source IP, or some other attribute
+// of a Dialer for a Transfer, you can do so by specifying the attributes
+// in the Transfer.Conn:
+//
+//     d := net.Dialer{LocalAddr: transfer_source}
+//     con, err := d.Dial("tcp", master)
+//     dnscon := &dns.Conn{Conn:con}
+//     transfer = &dns.Transfer{Conn: dnscon}
+//     channel, err := transfer.In(message, master)
+//
+func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
+       switch q.Question[0].Qtype {
+       case TypeAXFR, TypeIXFR:
+       default:
+               return nil, &Error{"unsupported question type"}
+       }
+
+       timeout := dnsTimeout
+       if t.DialTimeout != 0 {
+               timeout = t.DialTimeout
+       }
+
+       if t.Conn == nil {
+               t.Conn, err = DialTimeout("tcp", a, timeout)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       if err := t.WriteMsg(q); err != nil {
+               return nil, err
+       }
+
+       env = make(chan *Envelope)
+       switch q.Question[0].Qtype {
+       case TypeAXFR:
+               go t.inAxfr(q, env)
+       case TypeIXFR:
+               go t.inIxfr(q, env)
+       }
+
+       return env, nil
+}
+
+func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) {
+       first := true
+       defer t.Close()
+       defer close(c)
+       timeout := dnsTimeout
+       if t.ReadTimeout != 0 {
+               timeout = t.ReadTimeout
+       }
+       for {
+               t.Conn.SetReadDeadline(time.Now().Add(timeout))
+               in, err := t.ReadMsg()
+               if err != nil {
+                       c <- &Envelope{nil, err}
+                       return
+               }
+               if q.Id != in.Id {
+                       c <- &Envelope{in.Answer, ErrId}
+                       return
+               }
+               if first {
+                       if in.Rcode != RcodeSuccess {
+                               c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
+                               return
+                       }
+                       if !isSOAFirst(in) {
+                               c <- &Envelope{in.Answer, ErrSoa}
+                               return
+                       }
+                       first = !first
+                       // only one answer that is SOA, receive more
+                       if len(in.Answer) == 1 {
+                               t.tsigTimersOnly = true
+                               c <- &Envelope{in.Answer, nil}
+                               continue
+                       }
+               }
+
+               if !first {
+                       t.tsigTimersOnly = true // Subsequent envelopes use this.
+                       if isSOALast(in) {
+                               c <- &Envelope{in.Answer, nil}
+                               return
+                       }
+                       c <- &Envelope{in.Answer, nil}
+               }
+       }
+}
+
+func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
+       var serial uint32 // The first serial seen is the current server serial
+       axfr := true
+       n := 0
+       qser := q.Ns[0].(*SOA).Serial
+       defer t.Close()
+       defer close(c)
+       timeout := dnsTimeout
+       if t.ReadTimeout != 0 {
+               timeout = t.ReadTimeout
+       }
+       for {
+               t.SetReadDeadline(time.Now().Add(timeout))
+               in, err := t.ReadMsg()
+               if err != nil {
+                       c <- &Envelope{nil, err}
+                       return
+               }
+               if q.Id != in.Id {
+                       c <- &Envelope{in.Answer, ErrId}
+                       return
+               }
+               if in.Rcode != RcodeSuccess {
+                       c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}}
+                       return
+               }
+               if n == 0 {
+                       // Check if the returned answer is ok
+                       if !isSOAFirst(in) {
+                               c <- &Envelope{in.Answer, ErrSoa}
+                               return
+                       }
+                       // This serial is important
+                       serial = in.Answer[0].(*SOA).Serial
+                       // Check if there are no changes in zone
+                       if qser >= serial {
+                               c <- &Envelope{in.Answer, nil}
+                               return
+                       }
+               }
+               // Now we need to check each message for SOA records, to see what we need to do
+               t.tsigTimersOnly = true
+               for _, rr := range in.Answer {
+                       if v, ok := rr.(*SOA); ok {
+                               if v.Serial == serial {
+                                       n++
+                                       // quit if it's a full axfr or the the servers' SOA is repeated the third time
+                                       if axfr && n == 2 || n == 3 {
+                                               c <- &Envelope{in.Answer, nil}
+                                               return
+                                       }
+                               } else if axfr {
+                                       // it's an ixfr
+                                       axfr = false
+                               }
+                       }
+               }
+               c <- &Envelope{in.Answer, nil}
+       }
+}
+
+// Out performs an outgoing transfer with the client connecting in w.
+// Basic use pattern:
+//
+//     ch := make(chan *dns.Envelope)
+//     tr := new(dns.Transfer)
+//     go tr.Out(w, r, ch)
+//     ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
+//     close(ch)
+//     w.Hijack()
+//     // w.Close() // Client closes connection
+//
+// The server is responsible for sending the correct sequence of RRs through the
+// channel ch.
+func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
+       for x := range ch {
+               r := new(Msg)
+               // Compress?
+               r.SetReply(q)
+               r.Authoritative = true
+               // assume it fits TODO(miek): fix
+               r.Answer = append(r.Answer, x.RR...)
+               if err := w.WriteMsg(r); err != nil {
+                       return err
+               }
+       }
+       w.TsigTimersOnly(true)
+       return nil
+}
+
+// ReadMsg reads a message from the transfer connection t.
+func (t *Transfer) ReadMsg() (*Msg, error) {
+       m := new(Msg)
+       p := make([]byte, MaxMsgSize)
+       n, err := t.Read(p)
+       if err != nil && n == 0 {
+               return nil, err
+       }
+       p = p[:n]
+       if err := m.Unpack(p); err != nil {
+               return nil, err
+       }
+       if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
+               if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
+                       return m, ErrSecret
+               }
+               // Need to work on the original message p, as that was used to calculate the tsig.
+               err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
+               t.tsigRequestMAC = ts.MAC
+       }
+       return m, err
+}
+
+// WriteMsg writes a message through the transfer connection t.
+func (t *Transfer) WriteMsg(m *Msg) (err error) {
+       var out []byte
+       if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil {
+               if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok {
+                       return ErrSecret
+               }
+               out, t.tsigRequestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly)
+       } else {
+               out, err = m.Pack()
+       }
+       if err != nil {
+               return err
+       }
+       _, err = t.Write(out)
+       return err
+}
+
+func isSOAFirst(in *Msg) bool {
+       return len(in.Answer) > 0 &&
+               in.Answer[0].Header().Rrtype == TypeSOA
+}
+
+func isSOALast(in *Msg) bool {
+       return len(in.Answer) > 0 &&
+               in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
+}
+
+const errXFR = "bad xfr rcode: %d"
diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go
new file mode 100644 (file)
index 0000000..7438916
--- /dev/null
@@ -0,0 +1,1140 @@
+// Code generated by "go run duplicate_generate.go"; DO NOT EDIT.
+
+package dns
+
+// isDuplicate() functions
+
+func (r1 *A) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*A)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !r1.A.Equal(r2.A) {
+               return false
+       }
+       return true
+}
+
+func (r1 *AAAA) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*AAAA)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !r1.AAAA.Equal(r2.AAAA) {
+               return false
+       }
+       return true
+}
+
+func (r1 *AFSDB) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*AFSDB)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Subtype != r2.Subtype {
+               return false
+       }
+       if !isDuplicateName(r1.Hostname, r2.Hostname) {
+               return false
+       }
+       return true
+}
+
+func (r1 *ANY) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*ANY)
+       if !ok {
+               return false
+       }
+       _ = r2
+       return true
+}
+
+func (r1 *AVC) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*AVC)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if len(r1.Txt) != len(r2.Txt) {
+               return false
+       }
+       for i := 0; i < len(r1.Txt); i++ {
+               if r1.Txt[i] != r2.Txt[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *CAA) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*CAA)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Flag != r2.Flag {
+               return false
+       }
+       if r1.Tag != r2.Tag {
+               return false
+       }
+       if r1.Value != r2.Value {
+               return false
+       }
+       return true
+}
+
+func (r1 *CERT) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*CERT)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Type != r2.Type {
+               return false
+       }
+       if r1.KeyTag != r2.KeyTag {
+               return false
+       }
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.Certificate != r2.Certificate {
+               return false
+       }
+       return true
+}
+
+func (r1 *CNAME) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*CNAME)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Target, r2.Target) {
+               return false
+       }
+       return true
+}
+
+func (r1 *CSYNC) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*CSYNC)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Serial != r2.Serial {
+               return false
+       }
+       if r1.Flags != r2.Flags {
+               return false
+       }
+       if len(r1.TypeBitMap) != len(r2.TypeBitMap) {
+               return false
+       }
+       for i := 0; i < len(r1.TypeBitMap); i++ {
+               if r1.TypeBitMap[i] != r2.TypeBitMap[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *DHCID) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*DHCID)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Digest != r2.Digest {
+               return false
+       }
+       return true
+}
+
+func (r1 *DNAME) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*DNAME)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Target, r2.Target) {
+               return false
+       }
+       return true
+}
+
+func (r1 *DNSKEY) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*DNSKEY)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Flags != r2.Flags {
+               return false
+       }
+       if r1.Protocol != r2.Protocol {
+               return false
+       }
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.PublicKey != r2.PublicKey {
+               return false
+       }
+       return true
+}
+
+func (r1 *DS) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*DS)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.KeyTag != r2.KeyTag {
+               return false
+       }
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.DigestType != r2.DigestType {
+               return false
+       }
+       if r1.Digest != r2.Digest {
+               return false
+       }
+       return true
+}
+
+func (r1 *EID) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*EID)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Endpoint != r2.Endpoint {
+               return false
+       }
+       return true
+}
+
+func (r1 *EUI48) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*EUI48)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Address != r2.Address {
+               return false
+       }
+       return true
+}
+
+func (r1 *EUI64) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*EUI64)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Address != r2.Address {
+               return false
+       }
+       return true
+}
+
+func (r1 *GID) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*GID)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Gid != r2.Gid {
+               return false
+       }
+       return true
+}
+
+func (r1 *GPOS) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*GPOS)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Longitude != r2.Longitude {
+               return false
+       }
+       if r1.Latitude != r2.Latitude {
+               return false
+       }
+       if r1.Altitude != r2.Altitude {
+               return false
+       }
+       return true
+}
+
+func (r1 *HINFO) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*HINFO)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Cpu != r2.Cpu {
+               return false
+       }
+       if r1.Os != r2.Os {
+               return false
+       }
+       return true
+}
+
+func (r1 *HIP) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*HIP)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.HitLength != r2.HitLength {
+               return false
+       }
+       if r1.PublicKeyAlgorithm != r2.PublicKeyAlgorithm {
+               return false
+       }
+       if r1.PublicKeyLength != r2.PublicKeyLength {
+               return false
+       }
+       if r1.Hit != r2.Hit {
+               return false
+       }
+       if r1.PublicKey != r2.PublicKey {
+               return false
+       }
+       if len(r1.RendezvousServers) != len(r2.RendezvousServers) {
+               return false
+       }
+       for i := 0; i < len(r1.RendezvousServers); i++ {
+               if !isDuplicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *KX) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*KX)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if !isDuplicateName(r1.Exchanger, r2.Exchanger) {
+               return false
+       }
+       return true
+}
+
+func (r1 *L32) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*L32)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if !r1.Locator32.Equal(r2.Locator32) {
+               return false
+       }
+       return true
+}
+
+func (r1 *L64) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*L64)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if r1.Locator64 != r2.Locator64 {
+               return false
+       }
+       return true
+}
+
+func (r1 *LOC) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*LOC)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Version != r2.Version {
+               return false
+       }
+       if r1.Size != r2.Size {
+               return false
+       }
+       if r1.HorizPre != r2.HorizPre {
+               return false
+       }
+       if r1.VertPre != r2.VertPre {
+               return false
+       }
+       if r1.Latitude != r2.Latitude {
+               return false
+       }
+       if r1.Longitude != r2.Longitude {
+               return false
+       }
+       if r1.Altitude != r2.Altitude {
+               return false
+       }
+       return true
+}
+
+func (r1 *LP) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*LP)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if !isDuplicateName(r1.Fqdn, r2.Fqdn) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MB) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MB)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Mb, r2.Mb) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MD) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MD)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Md, r2.Md) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MF) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MF)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Mf, r2.Mf) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MG) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MG)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Mg, r2.Mg) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MINFO) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MINFO)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Rmail, r2.Rmail) {
+               return false
+       }
+       if !isDuplicateName(r1.Email, r2.Email) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MR) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MR)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Mr, r2.Mr) {
+               return false
+       }
+       return true
+}
+
+func (r1 *MX) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*MX)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if !isDuplicateName(r1.Mx, r2.Mx) {
+               return false
+       }
+       return true
+}
+
+func (r1 *NAPTR) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NAPTR)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Order != r2.Order {
+               return false
+       }
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if r1.Flags != r2.Flags {
+               return false
+       }
+       if r1.Service != r2.Service {
+               return false
+       }
+       if r1.Regexp != r2.Regexp {
+               return false
+       }
+       if !isDuplicateName(r1.Replacement, r2.Replacement) {
+               return false
+       }
+       return true
+}
+
+func (r1 *NID) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NID)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if r1.NodeID != r2.NodeID {
+               return false
+       }
+       return true
+}
+
+func (r1 *NIMLOC) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NIMLOC)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Locator != r2.Locator {
+               return false
+       }
+       return true
+}
+
+func (r1 *NINFO) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NINFO)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if len(r1.ZSData) != len(r2.ZSData) {
+               return false
+       }
+       for i := 0; i < len(r1.ZSData); i++ {
+               if r1.ZSData[i] != r2.ZSData[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *NS) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NS)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Ns, r2.Ns) {
+               return false
+       }
+       return true
+}
+
+func (r1 *NSAPPTR) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NSAPPTR)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Ptr, r2.Ptr) {
+               return false
+       }
+       return true
+}
+
+func (r1 *NSEC) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NSEC)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.NextDomain, r2.NextDomain) {
+               return false
+       }
+       if len(r1.TypeBitMap) != len(r2.TypeBitMap) {
+               return false
+       }
+       for i := 0; i < len(r1.TypeBitMap); i++ {
+               if r1.TypeBitMap[i] != r2.TypeBitMap[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *NSEC3) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NSEC3)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Hash != r2.Hash {
+               return false
+       }
+       if r1.Flags != r2.Flags {
+               return false
+       }
+       if r1.Iterations != r2.Iterations {
+               return false
+       }
+       if r1.SaltLength != r2.SaltLength {
+               return false
+       }
+       if r1.Salt != r2.Salt {
+               return false
+       }
+       if r1.HashLength != r2.HashLength {
+               return false
+       }
+       if r1.NextDomain != r2.NextDomain {
+               return false
+       }
+       if len(r1.TypeBitMap) != len(r2.TypeBitMap) {
+               return false
+       }
+       for i := 0; i < len(r1.TypeBitMap); i++ {
+               if r1.TypeBitMap[i] != r2.TypeBitMap[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *NSEC3PARAM) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NSEC3PARAM)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Hash != r2.Hash {
+               return false
+       }
+       if r1.Flags != r2.Flags {
+               return false
+       }
+       if r1.Iterations != r2.Iterations {
+               return false
+       }
+       if r1.SaltLength != r2.SaltLength {
+               return false
+       }
+       if r1.Salt != r2.Salt {
+               return false
+       }
+       return true
+}
+
+func (r1 *NULL) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*NULL)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Data != r2.Data {
+               return false
+       }
+       return true
+}
+
+func (r1 *OPENPGPKEY) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*OPENPGPKEY)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.PublicKey != r2.PublicKey {
+               return false
+       }
+       return true
+}
+
+func (r1 *PTR) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*PTR)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Ptr, r2.Ptr) {
+               return false
+       }
+       return true
+}
+
+func (r1 *PX) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*PX)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if !isDuplicateName(r1.Map822, r2.Map822) {
+               return false
+       }
+       if !isDuplicateName(r1.Mapx400, r2.Mapx400) {
+               return false
+       }
+       return true
+}
+
+func (r1 *RFC3597) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*RFC3597)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Rdata != r2.Rdata {
+               return false
+       }
+       return true
+}
+
+func (r1 *RKEY) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*RKEY)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Flags != r2.Flags {
+               return false
+       }
+       if r1.Protocol != r2.Protocol {
+               return false
+       }
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.PublicKey != r2.PublicKey {
+               return false
+       }
+       return true
+}
+
+func (r1 *RP) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*RP)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Mbox, r2.Mbox) {
+               return false
+       }
+       if !isDuplicateName(r1.Txt, r2.Txt) {
+               return false
+       }
+       return true
+}
+
+func (r1 *RRSIG) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*RRSIG)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.TypeCovered != r2.TypeCovered {
+               return false
+       }
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.Labels != r2.Labels {
+               return false
+       }
+       if r1.OrigTtl != r2.OrigTtl {
+               return false
+       }
+       if r1.Expiration != r2.Expiration {
+               return false
+       }
+       if r1.Inception != r2.Inception {
+               return false
+       }
+       if r1.KeyTag != r2.KeyTag {
+               return false
+       }
+       if !isDuplicateName(r1.SignerName, r2.SignerName) {
+               return false
+       }
+       if r1.Signature != r2.Signature {
+               return false
+       }
+       return true
+}
+
+func (r1 *RT) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*RT)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Preference != r2.Preference {
+               return false
+       }
+       if !isDuplicateName(r1.Host, r2.Host) {
+               return false
+       }
+       return true
+}
+
+func (r1 *SMIMEA) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*SMIMEA)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Usage != r2.Usage {
+               return false
+       }
+       if r1.Selector != r2.Selector {
+               return false
+       }
+       if r1.MatchingType != r2.MatchingType {
+               return false
+       }
+       if r1.Certificate != r2.Certificate {
+               return false
+       }
+       return true
+}
+
+func (r1 *SOA) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*SOA)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Ns, r2.Ns) {
+               return false
+       }
+       if !isDuplicateName(r1.Mbox, r2.Mbox) {
+               return false
+       }
+       if r1.Serial != r2.Serial {
+               return false
+       }
+       if r1.Refresh != r2.Refresh {
+               return false
+       }
+       if r1.Retry != r2.Retry {
+               return false
+       }
+       if r1.Expire != r2.Expire {
+               return false
+       }
+       if r1.Minttl != r2.Minttl {
+               return false
+       }
+       return true
+}
+
+func (r1 *SPF) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*SPF)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if len(r1.Txt) != len(r2.Txt) {
+               return false
+       }
+       for i := 0; i < len(r1.Txt); i++ {
+               if r1.Txt[i] != r2.Txt[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *SRV) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*SRV)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Priority != r2.Priority {
+               return false
+       }
+       if r1.Weight != r2.Weight {
+               return false
+       }
+       if r1.Port != r2.Port {
+               return false
+       }
+       if !isDuplicateName(r1.Target, r2.Target) {
+               return false
+       }
+       return true
+}
+
+func (r1 *SSHFP) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*SSHFP)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.Type != r2.Type {
+               return false
+       }
+       if r1.FingerPrint != r2.FingerPrint {
+               return false
+       }
+       return true
+}
+
+func (r1 *TA) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*TA)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.KeyTag != r2.KeyTag {
+               return false
+       }
+       if r1.Algorithm != r2.Algorithm {
+               return false
+       }
+       if r1.DigestType != r2.DigestType {
+               return false
+       }
+       if r1.Digest != r2.Digest {
+               return false
+       }
+       return true
+}
+
+func (r1 *TALINK) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*TALINK)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.PreviousName, r2.PreviousName) {
+               return false
+       }
+       if !isDuplicateName(r1.NextName, r2.NextName) {
+               return false
+       }
+       return true
+}
+
+func (r1 *TKEY) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*TKEY)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Algorithm, r2.Algorithm) {
+               return false
+       }
+       if r1.Inception != r2.Inception {
+               return false
+       }
+       if r1.Expiration != r2.Expiration {
+               return false
+       }
+       if r1.Mode != r2.Mode {
+               return false
+       }
+       if r1.Error != r2.Error {
+               return false
+       }
+       if r1.KeySize != r2.KeySize {
+               return false
+       }
+       if r1.Key != r2.Key {
+               return false
+       }
+       if r1.OtherLen != r2.OtherLen {
+               return false
+       }
+       if r1.OtherData != r2.OtherData {
+               return false
+       }
+       return true
+}
+
+func (r1 *TLSA) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*TLSA)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Usage != r2.Usage {
+               return false
+       }
+       if r1.Selector != r2.Selector {
+               return false
+       }
+       if r1.MatchingType != r2.MatchingType {
+               return false
+       }
+       if r1.Certificate != r2.Certificate {
+               return false
+       }
+       return true
+}
+
+func (r1 *TSIG) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*TSIG)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if !isDuplicateName(r1.Algorithm, r2.Algorithm) {
+               return false
+       }
+       if r1.TimeSigned != r2.TimeSigned {
+               return false
+       }
+       if r1.Fudge != r2.Fudge {
+               return false
+       }
+       if r1.MACSize != r2.MACSize {
+               return false
+       }
+       if r1.MAC != r2.MAC {
+               return false
+       }
+       if r1.OrigId != r2.OrigId {
+               return false
+       }
+       if r1.Error != r2.Error {
+               return false
+       }
+       if r1.OtherLen != r2.OtherLen {
+               return false
+       }
+       if r1.OtherData != r2.OtherData {
+               return false
+       }
+       return true
+}
+
+func (r1 *TXT) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*TXT)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if len(r1.Txt) != len(r2.Txt) {
+               return false
+       }
+       for i := 0; i < len(r1.Txt); i++ {
+               if r1.Txt[i] != r2.Txt[i] {
+                       return false
+               }
+       }
+       return true
+}
+
+func (r1 *UID) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*UID)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Uid != r2.Uid {
+               return false
+       }
+       return true
+}
+
+func (r1 *UINFO) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*UINFO)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Uinfo != r2.Uinfo {
+               return false
+       }
+       return true
+}
+
+func (r1 *URI) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*URI)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.Priority != r2.Priority {
+               return false
+       }
+       if r1.Weight != r2.Weight {
+               return false
+       }
+       if r1.Target != r2.Target {
+               return false
+       }
+       return true
+}
+
+func (r1 *X25) isDuplicate(_r2 RR) bool {
+       r2, ok := _r2.(*X25)
+       if !ok {
+               return false
+       }
+       _ = r2
+       if r1.PSDNAddress != r2.PSDNAddress {
+               return false
+       }
+       return true
+}
diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go
new file mode 100644 (file)
index 0000000..c4cf475
--- /dev/null
@@ -0,0 +1,2722 @@
+// Code generated by "go run msg_generate.go"; DO NOT EDIT.
+
+package dns
+
+// pack*() functions
+
+func (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDataA(rr.A, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *AAAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDataAAAA(rr.AAAA, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Subtype, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Hostname, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       return off, nil
+}
+
+func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringTxt(rr.Txt, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Flag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Tag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringOctet(rr.Value, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CDNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Protocol, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CDS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.DigestType, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Digest, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CERT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Type, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.Certificate, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Target, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CSYNC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint32(rr.Serial, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDataNsec(rr.TypeBitMap, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DHCID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringBase64(rr.Digest, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DLV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.DigestType, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Digest, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Target, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Protocol, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.DigestType, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Digest, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *EID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringHex(rr.Endpoint, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *EUI48) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint48(rr.Address, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *EUI64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint64(rr.Address, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *GID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint32(rr.Gid, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *GPOS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packString(rr.Longitude, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Latitude, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Altitude, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *HINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packString(rr.Cpu, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Os, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.HitLength, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.PublicKeyAlgorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.PublicKeyLength, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Hit, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Protocol, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *KX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Exchanger, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *L32) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDataA(rr.Locator32, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *L64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint64(rr.Locator64, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *LOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Version, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Size, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.HorizPre, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.VertPre, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Latitude, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Longitude, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Altitude, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *LP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Fqdn, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Mb, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Md, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Mf, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Mg, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Rmail, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Email, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Mr, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Mx, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NAPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Order, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Service, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packString(rr.Regexp, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Replacement, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint64(rr.NodeID, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NIMLOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringHex(rr.Locator, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringTxt(rr.ZSData, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Ns, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSAPPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Ptr, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSEC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.NextDomain, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDataNsec(rr.TypeBitMap, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSEC3) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Hash, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Iterations, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.SaltLength, msg, off)
+       if err != nil {
+               return off, err
+       }
+       // Only pack salt if value is not "-", i.e. empty
+       if rr.Salt != "-" {
+               off, err = packStringHex(rr.Salt, msg, off)
+               if err != nil {
+                       return off, err
+               }
+       }
+       off, err = packUint8(rr.HashLength, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase32(rr.NextDomain, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDataNsec(rr.TypeBitMap, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Hash, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Iterations, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.SaltLength, msg, off)
+       if err != nil {
+               return off, err
+       }
+       // Only pack salt if value is not "-", i.e. empty
+       if rr.Salt != "-" {
+               off, err = packStringHex(rr.Salt, msg, off)
+               if err != nil {
+                       return off, err
+               }
+       }
+       return off, nil
+}
+
+func (rr *NULL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringAny(rr.Data, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringBase64(rr.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *OPT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDataOpt(rr.Option, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *PTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Ptr, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *PX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Map822, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Mapx400, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RFC3597) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringHex(rr.Rdata, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Flags, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Protocol, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.PublicKey, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Mbox, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Txt, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RRSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.TypeCovered, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Labels, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.OrigTtl, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Expiration, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Inception, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.SignerName, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.Signature, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Preference, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Host, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.TypeCovered, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Labels, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.OrigTtl, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Expiration, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Inception, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.SignerName, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringBase64(rr.Signature, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SMIMEA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Usage, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Selector, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.MatchingType, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Certificate, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SOA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Ns, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Mbox, msg, off, compression, compress)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Serial, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Refresh, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Retry, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Expire, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Minttl, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SPF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringTxt(rr.Txt, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SRV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Priority, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Weight, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Port, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.Target, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Type, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.FingerPrint, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.KeyTag, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Algorithm, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.DigestType, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Digest, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TALINK) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.PreviousName, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packDomainName(rr.NextName, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Algorithm, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Inception, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint32(rr.Expiration, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Mode, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Error, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.KeySize, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Key, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.OtherLen, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.OtherData, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TLSA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint8(rr.Usage, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.Selector, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint8(rr.MatchingType, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.Certificate, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packDomainName(rr.Algorithm, msg, off, compression, false)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint48(rr.TimeSigned, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Fudge, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.MACSize, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.MAC, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.OrigId, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Error, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.OtherLen, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringHex(rr.OtherData, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packStringTxt(rr.Txt, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *UID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint32(rr.Uid, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *UINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packString(rr.Uinfo, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *URI) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packUint16(rr.Priority, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packUint16(rr.Weight, msg, off)
+       if err != nil {
+               return off, err
+       }
+       off, err = packStringOctet(rr.Target, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
+       off, err = packString(rr.PSDNAddress, msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+// unpack*() functions
+
+func (rr *A) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.A, off, err = unpackDataA(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *AAAA) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.AAAA, off, err = unpackDataAAAA(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Subtype, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Hostname, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       return off, nil
+}
+
+func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Txt, off, err = unpackStringTxt(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CAA) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Flag, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Tag, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Value, off, err = unpackStringOctet(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CDNSKEY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Flags, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Protocol, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CDS) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.DigestType, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CERT) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Type, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Certificate, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CNAME) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Target, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *CSYNC) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Serial, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Flags, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.TypeBitMap, off, err = unpackDataNsec(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DHCID) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Digest, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DLV) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.DigestType, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DNAME) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Target, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DNSKEY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Flags, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Protocol, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *DS) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.DigestType, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *EID) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Endpoint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *EUI48) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Address, off, err = unpackUint48(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *EUI64) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Address, off, err = unpackUint64(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *GID) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Gid, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *GPOS) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Longitude, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Latitude, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Altitude, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *HINFO) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Cpu, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Os, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.HitLength, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.PublicKeyAlgorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.PublicKeyLength, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Hit, off, err = unpackStringHex(msg, off, off+int(rr.HitLength))
+       if err != nil {
+               return off, err
+       }
+       rr.PublicKey, off, err = unpackStringBase64(msg, off, off+int(rr.PublicKeyLength))
+       if err != nil {
+               return off, err
+       }
+       rr.RendezvousServers, off, err = unpackDataDomainNames(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Flags, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Protocol, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *KX) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Exchanger, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *L32) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Locator32, off, err = unpackDataA(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *L64) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Locator64, off, err = unpackUint64(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *LOC) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Version, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Size, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.HorizPre, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.VertPre, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Latitude, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Longitude, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Altitude, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *LP) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Fqdn, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MB) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Mb, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MD) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Md, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MF) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Mf, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MG) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Mg, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MINFO) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Rmail, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Email, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MR) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Mr, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *MX) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Mx, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NAPTR) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Order, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Flags, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Service, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Regexp, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Replacement, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NID) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.NodeID, off, err = unpackUint64(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NIMLOC) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Locator, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NINFO) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.ZSData, off, err = unpackStringTxt(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NS) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Ns, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSAPPTR) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Ptr, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSEC) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.NextDomain, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.TypeBitMap, off, err = unpackDataNsec(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Hash, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Flags, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Iterations, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.SaltLength, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength))
+       if err != nil {
+               return off, err
+       }
+       rr.HashLength, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.NextDomain, off, err = unpackStringBase32(msg, off, off+int(rr.HashLength))
+       if err != nil {
+               return off, err
+       }
+       rr.TypeBitMap, off, err = unpackDataNsec(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NSEC3PARAM) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Hash, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Flags, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Iterations, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.SaltLength, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *NULL) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Data, off, err = unpackStringAny(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *OPENPGPKEY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *OPT) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Option, off, err = unpackDataOpt(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *PTR) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Ptr, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *PX) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Map822, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Mapx400, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RFC3597) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Rdata, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RKEY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Flags, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Protocol, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RP) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Mbox, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Txt, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RRSIG) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.TypeCovered, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Labels, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.OrigTtl, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Expiration, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Inception, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.SignerName, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *RT) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Preference, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Host, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SIG) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.TypeCovered, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Labels, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.OrigTtl, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Expiration, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Inception, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.SignerName, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SMIMEA) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Usage, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Selector, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.MatchingType, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SOA) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Ns, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Mbox, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Serial, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Refresh, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Retry, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Expire, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Minttl, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SPF) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Txt, off, err = unpackStringTxt(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SRV) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Priority, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Weight, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Port, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Target, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *SSHFP) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Type, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.FingerPrint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TA) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.KeyTag, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Algorithm, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.DigestType, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TALINK) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.PreviousName, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.NextName, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TKEY) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Algorithm, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Inception, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Expiration, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Mode, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Error, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.KeySize, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Key, off, err = unpackStringHex(msg, off, off+int(rr.KeySize))
+       if err != nil {
+               return off, err
+       }
+       rr.OtherLen, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TLSA) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Usage, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Selector, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.MatchingType, off, err = unpackUint8(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TSIG) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Algorithm, off, err = UnpackDomainName(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.TimeSigned, off, err = unpackUint48(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Fudge, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.MACSize, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.MAC, off, err = unpackStringHex(msg, off, off+int(rr.MACSize))
+       if err != nil {
+               return off, err
+       }
+       rr.OrigId, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Error, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.OtherLen, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen))
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *TXT) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Txt, off, err = unpackStringTxt(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *UID) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Uid, off, err = unpackUint32(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *UINFO) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Uinfo, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *URI) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.Priority, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Weight, off, err = unpackUint16(msg, off)
+       if err != nil {
+               return off, err
+       }
+       if off == len(msg) {
+               return off, nil
+       }
+       rr.Target, off, err = unpackStringOctet(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
+
+func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) {
+       rdStart := off
+       _ = rdStart
+
+       rr.PSDNAddress, off, err = unpackString(msg, off)
+       if err != nil {
+               return off, err
+       }
+       return off, nil
+}
diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go
new file mode 100644 (file)
index 0000000..19a542d
--- /dev/null
@@ -0,0 +1,875 @@
+// Code generated by "go run types_generate.go"; DO NOT EDIT.
+
+package dns
+
+import (
+       "encoding/base64"
+       "net"
+)
+
+// TypeToRR is a map of constructors for each RR type.
+var TypeToRR = map[uint16]func() RR{
+       TypeA:          func() RR { return new(A) },
+       TypeAAAA:       func() RR { return new(AAAA) },
+       TypeAFSDB:      func() RR { return new(AFSDB) },
+       TypeANY:        func() RR { return new(ANY) },
+       TypeAVC:        func() RR { return new(AVC) },
+       TypeCAA:        func() RR { return new(CAA) },
+       TypeCDNSKEY:    func() RR { return new(CDNSKEY) },
+       TypeCDS:        func() RR { return new(CDS) },
+       TypeCERT:       func() RR { return new(CERT) },
+       TypeCNAME:      func() RR { return new(CNAME) },
+       TypeCSYNC:      func() RR { return new(CSYNC) },
+       TypeDHCID:      func() RR { return new(DHCID) },
+       TypeDLV:        func() RR { return new(DLV) },
+       TypeDNAME:      func() RR { return new(DNAME) },
+       TypeDNSKEY:     func() RR { return new(DNSKEY) },
+       TypeDS:         func() RR { return new(DS) },
+       TypeEID:        func() RR { return new(EID) },
+       TypeEUI48:      func() RR { return new(EUI48) },
+       TypeEUI64:      func() RR { return new(EUI64) },
+       TypeGID:        func() RR { return new(GID) },
+       TypeGPOS:       func() RR { return new(GPOS) },
+       TypeHINFO:      func() RR { return new(HINFO) },
+       TypeHIP:        func() RR { return new(HIP) },
+       TypeKEY:        func() RR { return new(KEY) },
+       TypeKX:         func() RR { return new(KX) },
+       TypeL32:        func() RR { return new(L32) },
+       TypeL64:        func() RR { return new(L64) },
+       TypeLOC:        func() RR { return new(LOC) },
+       TypeLP:         func() RR { return new(LP) },
+       TypeMB:         func() RR { return new(MB) },
+       TypeMD:         func() RR { return new(MD) },
+       TypeMF:         func() RR { return new(MF) },
+       TypeMG:         func() RR { return new(MG) },
+       TypeMINFO:      func() RR { return new(MINFO) },
+       TypeMR:         func() RR { return new(MR) },
+       TypeMX:         func() RR { return new(MX) },
+       TypeNAPTR:      func() RR { return new(NAPTR) },
+       TypeNID:        func() RR { return new(NID) },
+       TypeNIMLOC:     func() RR { return new(NIMLOC) },
+       TypeNINFO:      func() RR { return new(NINFO) },
+       TypeNS:         func() RR { return new(NS) },
+       TypeNSAPPTR:    func() RR { return new(NSAPPTR) },
+       TypeNSEC:       func() RR { return new(NSEC) },
+       TypeNSEC3:      func() RR { return new(NSEC3) },
+       TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
+       TypeNULL:       func() RR { return new(NULL) },
+       TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
+       TypeOPT:        func() RR { return new(OPT) },
+       TypePTR:        func() RR { return new(PTR) },
+       TypePX:         func() RR { return new(PX) },
+       TypeRKEY:       func() RR { return new(RKEY) },
+       TypeRP:         func() RR { return new(RP) },
+       TypeRRSIG:      func() RR { return new(RRSIG) },
+       TypeRT:         func() RR { return new(RT) },
+       TypeSIG:        func() RR { return new(SIG) },
+       TypeSMIMEA:     func() RR { return new(SMIMEA) },
+       TypeSOA:        func() RR { return new(SOA) },
+       TypeSPF:        func() RR { return new(SPF) },
+       TypeSRV:        func() RR { return new(SRV) },
+       TypeSSHFP:      func() RR { return new(SSHFP) },
+       TypeTA:         func() RR { return new(TA) },
+       TypeTALINK:     func() RR { return new(TALINK) },
+       TypeTKEY:       func() RR { return new(TKEY) },
+       TypeTLSA:       func() RR { return new(TLSA) },
+       TypeTSIG:       func() RR { return new(TSIG) },
+       TypeTXT:        func() RR { return new(TXT) },
+       TypeUID:        func() RR { return new(UID) },
+       TypeUINFO:      func() RR { return new(UINFO) },
+       TypeURI:        func() RR { return new(URI) },
+       TypeX25:        func() RR { return new(X25) },
+}
+
+// TypeToString is a map of strings for each RR type.
+var TypeToString = map[uint16]string{
+       TypeA:          "A",
+       TypeAAAA:       "AAAA",
+       TypeAFSDB:      "AFSDB",
+       TypeANY:        "ANY",
+       TypeATMA:       "ATMA",
+       TypeAVC:        "AVC",
+       TypeAXFR:       "AXFR",
+       TypeCAA:        "CAA",
+       TypeCDNSKEY:    "CDNSKEY",
+       TypeCDS:        "CDS",
+       TypeCERT:       "CERT",
+       TypeCNAME:      "CNAME",
+       TypeCSYNC:      "CSYNC",
+       TypeDHCID:      "DHCID",
+       TypeDLV:        "DLV",
+       TypeDNAME:      "DNAME",
+       TypeDNSKEY:     "DNSKEY",
+       TypeDS:         "DS",
+       TypeEID:        "EID",
+       TypeEUI48:      "EUI48",
+       TypeEUI64:      "EUI64",
+       TypeGID:        "GID",
+       TypeGPOS:       "GPOS",
+       TypeHINFO:      "HINFO",
+       TypeHIP:        "HIP",
+       TypeISDN:       "ISDN",
+       TypeIXFR:       "IXFR",
+       TypeKEY:        "KEY",
+       TypeKX:         "KX",
+       TypeL32:        "L32",
+       TypeL64:        "L64",
+       TypeLOC:        "LOC",
+       TypeLP:         "LP",
+       TypeMAILA:      "MAILA",
+       TypeMAILB:      "MAILB",
+       TypeMB:         "MB",
+       TypeMD:         "MD",
+       TypeMF:         "MF",
+       TypeMG:         "MG",
+       TypeMINFO:      "MINFO",
+       TypeMR:         "MR",
+       TypeMX:         "MX",
+       TypeNAPTR:      "NAPTR",
+       TypeNID:        "NID",
+       TypeNIMLOC:     "NIMLOC",
+       TypeNINFO:      "NINFO",
+       TypeNS:         "NS",
+       TypeNSEC:       "NSEC",
+       TypeNSEC3:      "NSEC3",
+       TypeNSEC3PARAM: "NSEC3PARAM",
+       TypeNULL:       "NULL",
+       TypeNXT:        "NXT",
+       TypeNone:       "None",
+       TypeOPENPGPKEY: "OPENPGPKEY",
+       TypeOPT:        "OPT",
+       TypePTR:        "PTR",
+       TypePX:         "PX",
+       TypeRKEY:       "RKEY",
+       TypeRP:         "RP",
+       TypeRRSIG:      "RRSIG",
+       TypeRT:         "RT",
+       TypeReserved:   "Reserved",
+       TypeSIG:        "SIG",
+       TypeSMIMEA:     "SMIMEA",
+       TypeSOA:        "SOA",
+       TypeSPF:        "SPF",
+       TypeSRV:        "SRV",
+       TypeSSHFP:      "SSHFP",
+       TypeTA:         "TA",
+       TypeTALINK:     "TALINK",
+       TypeTKEY:       "TKEY",
+       TypeTLSA:       "TLSA",
+       TypeTSIG:       "TSIG",
+       TypeTXT:        "TXT",
+       TypeUID:        "UID",
+       TypeUINFO:      "UINFO",
+       TypeUNSPEC:     "UNSPEC",
+       TypeURI:        "URI",
+       TypeX25:        "X25",
+       TypeNSAPPTR:    "NSAP-PTR",
+}
+
+func (rr *A) Header() *RR_Header          { return &rr.Hdr }
+func (rr *AAAA) Header() *RR_Header       { return &rr.Hdr }
+func (rr *AFSDB) Header() *RR_Header      { return &rr.Hdr }
+func (rr *ANY) Header() *RR_Header        { return &rr.Hdr }
+func (rr *AVC) Header() *RR_Header        { return &rr.Hdr }
+func (rr *CAA) Header() *RR_Header        { return &rr.Hdr }
+func (rr *CDNSKEY) Header() *RR_Header    { return &rr.Hdr }
+func (rr *CDS) Header() *RR_Header        { return &rr.Hdr }
+func (rr *CERT) Header() *RR_Header       { return &rr.Hdr }
+func (rr *CNAME) Header() *RR_Header      { return &rr.Hdr }
+func (rr *CSYNC) Header() *RR_Header      { return &rr.Hdr }
+func (rr *DHCID) Header() *RR_Header      { return &rr.Hdr }
+func (rr *DLV) Header() *RR_Header        { return &rr.Hdr }
+func (rr *DNAME) Header() *RR_Header      { return &rr.Hdr }
+func (rr *DNSKEY) Header() *RR_Header     { return &rr.Hdr }
+func (rr *DS) Header() *RR_Header         { return &rr.Hdr }
+func (rr *EID) Header() *RR_Header        { return &rr.Hdr }
+func (rr *EUI48) Header() *RR_Header      { return &rr.Hdr }
+func (rr *EUI64) Header() *RR_Header      { return &rr.Hdr }
+func (rr *GID) Header() *RR_Header        { return &rr.Hdr }
+func (rr *GPOS) Header() *RR_Header       { return &rr.Hdr }
+func (rr *HINFO) Header() *RR_Header      { return &rr.Hdr }
+func (rr *HIP) Header() *RR_Header        { return &rr.Hdr }
+func (rr *KEY) Header() *RR_Header        { return &rr.Hdr }
+func (rr *KX) Header() *RR_Header         { return &rr.Hdr }
+func (rr *L32) Header() *RR_Header        { return &rr.Hdr }
+func (rr *L64) Header() *RR_Header        { return &rr.Hdr }
+func (rr *LOC) Header() *RR_Header        { return &rr.Hdr }
+func (rr *LP) Header() *RR_Header         { return &rr.Hdr }
+func (rr *MB) Header() *RR_Header         { return &rr.Hdr }
+func (rr *MD) Header() *RR_Header         { return &rr.Hdr }
+func (rr *MF) Header() *RR_Header         { return &rr.Hdr }
+func (rr *MG) Header() *RR_Header         { return &rr.Hdr }
+func (rr *MINFO) Header() *RR_Header      { return &rr.Hdr }
+func (rr *MR) Header() *RR_Header         { return &rr.Hdr }
+func (rr *MX) Header() *RR_Header         { return &rr.Hdr }
+func (rr *NAPTR) Header() *RR_Header      { return &rr.Hdr }
+func (rr *NID) Header() *RR_Header        { return &rr.Hdr }
+func (rr *NIMLOC) Header() *RR_Header     { return &rr.Hdr }
+func (rr *NINFO) Header() *RR_Header      { return &rr.Hdr }
+func (rr *NS) Header() *RR_Header         { return &rr.Hdr }
+func (rr *NSAPPTR) Header() *RR_Header    { return &rr.Hdr }
+func (rr *NSEC) Header() *RR_Header       { return &rr.Hdr }
+func (rr *NSEC3) Header() *RR_Header      { return &rr.Hdr }
+func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
+func (rr *NULL) Header() *RR_Header       { return &rr.Hdr }
+func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
+func (rr *OPT) Header() *RR_Header        { return &rr.Hdr }
+func (rr *PTR) Header() *RR_Header        { return &rr.Hdr }
+func (rr *PX) Header() *RR_Header         { return &rr.Hdr }
+func (rr *RFC3597) Header() *RR_Header    { return &rr.Hdr }
+func (rr *RKEY) Header() *RR_Header       { return &rr.Hdr }
+func (rr *RP) Header() *RR_Header         { return &rr.Hdr }
+func (rr *RRSIG) Header() *RR_Header      { return &rr.Hdr }
+func (rr *RT) Header() *RR_Header         { return &rr.Hdr }
+func (rr *SIG) Header() *RR_Header        { return &rr.Hdr }
+func (rr *SMIMEA) Header() *RR_Header     { return &rr.Hdr }
+func (rr *SOA) Header() *RR_Header        { return &rr.Hdr }
+func (rr *SPF) Header() *RR_Header        { return &rr.Hdr }
+func (rr *SRV) Header() *RR_Header        { return &rr.Hdr }
+func (rr *SSHFP) Header() *RR_Header      { return &rr.Hdr }
+func (rr *TA) Header() *RR_Header         { return &rr.Hdr }
+func (rr *TALINK) Header() *RR_Header     { return &rr.Hdr }
+func (rr *TKEY) Header() *RR_Header       { return &rr.Hdr }
+func (rr *TLSA) Header() *RR_Header       { return &rr.Hdr }
+func (rr *TSIG) Header() *RR_Header       { return &rr.Hdr }
+func (rr *TXT) Header() *RR_Header        { return &rr.Hdr }
+func (rr *UID) Header() *RR_Header        { return &rr.Hdr }
+func (rr *UINFO) Header() *RR_Header      { return &rr.Hdr }
+func (rr *URI) Header() *RR_Header        { return &rr.Hdr }
+func (rr *X25) Header() *RR_Header        { return &rr.Hdr }
+
+// len() functions
+func (rr *A) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += net.IPv4len // A
+       return l
+}
+func (rr *AAAA) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += net.IPv6len // AAAA
+       return l
+}
+func (rr *AFSDB) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Subtype
+       l += domainNameLen(rr.Hostname, off+l, compression, false)
+       return l
+}
+func (rr *ANY) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       return l
+}
+func (rr *AVC) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       for _, x := range rr.Txt {
+               l += len(x) + 1
+       }
+       return l
+}
+func (rr *CAA) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++ // Flag
+       l += len(rr.Tag) + 1
+       l += len(rr.Value)
+       return l
+}
+func (rr *CERT) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Type
+       l += 2 // KeyTag
+       l++    // Algorithm
+       l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
+       return l
+}
+func (rr *CNAME) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Target, off+l, compression, true)
+       return l
+}
+func (rr *DHCID) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += base64.StdEncoding.DecodedLen(len(rr.Digest))
+       return l
+}
+func (rr *DNAME) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Target, off+l, compression, false)
+       return l
+}
+func (rr *DNSKEY) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Flags
+       l++    // Protocol
+       l++    // Algorithm
+       l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+       return l
+}
+func (rr *DS) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // KeyTag
+       l++    // Algorithm
+       l++    // DigestType
+       l += len(rr.Digest)/2 + 1
+       return l
+}
+func (rr *EID) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Endpoint)/2 + 1
+       return l
+}
+func (rr *EUI48) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 6 // Address
+       return l
+}
+func (rr *EUI64) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 8 // Address
+       return l
+}
+func (rr *GID) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 4 // Gid
+       return l
+}
+func (rr *GPOS) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Longitude) + 1
+       l += len(rr.Latitude) + 1
+       l += len(rr.Altitude) + 1
+       return l
+}
+func (rr *HINFO) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Cpu) + 1
+       l += len(rr.Os) + 1
+       return l
+}
+func (rr *HIP) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++    // HitLength
+       l++    // PublicKeyAlgorithm
+       l += 2 // PublicKeyLength
+       l += len(rr.Hit) / 2
+       l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+       for _, x := range rr.RendezvousServers {
+               l += domainNameLen(x, off+l, compression, false)
+       }
+       return l
+}
+func (rr *KX) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += domainNameLen(rr.Exchanger, off+l, compression, false)
+       return l
+}
+func (rr *L32) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2           // Preference
+       l += net.IPv4len // Locator32
+       return l
+}
+func (rr *L64) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += 8 // Locator64
+       return l
+}
+func (rr *LOC) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++    // Version
+       l++    // Size
+       l++    // HorizPre
+       l++    // VertPre
+       l += 4 // Latitude
+       l += 4 // Longitude
+       l += 4 // Altitude
+       return l
+}
+func (rr *LP) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += domainNameLen(rr.Fqdn, off+l, compression, false)
+       return l
+}
+func (rr *MB) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Mb, off+l, compression, true)
+       return l
+}
+func (rr *MD) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Md, off+l, compression, true)
+       return l
+}
+func (rr *MF) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Mf, off+l, compression, true)
+       return l
+}
+func (rr *MG) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Mg, off+l, compression, true)
+       return l
+}
+func (rr *MINFO) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Rmail, off+l, compression, true)
+       l += domainNameLen(rr.Email, off+l, compression, true)
+       return l
+}
+func (rr *MR) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Mr, off+l, compression, true)
+       return l
+}
+func (rr *MX) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += domainNameLen(rr.Mx, off+l, compression, true)
+       return l
+}
+func (rr *NAPTR) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Order
+       l += 2 // Preference
+       l += len(rr.Flags) + 1
+       l += len(rr.Service) + 1
+       l += len(rr.Regexp) + 1
+       l += domainNameLen(rr.Replacement, off+l, compression, false)
+       return l
+}
+func (rr *NID) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += 8 // NodeID
+       return l
+}
+func (rr *NIMLOC) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Locator)/2 + 1
+       return l
+}
+func (rr *NINFO) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       for _, x := range rr.ZSData {
+               l += len(x) + 1
+       }
+       return l
+}
+func (rr *NS) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Ns, off+l, compression, true)
+       return l
+}
+func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Ptr, off+l, compression, false)
+       return l
+}
+func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++    // Hash
+       l++    // Flags
+       l += 2 // Iterations
+       l++    // SaltLength
+       l += len(rr.Salt) / 2
+       return l
+}
+func (rr *NULL) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Data)
+       return l
+}
+func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+       return l
+}
+func (rr *PTR) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Ptr, off+l, compression, true)
+       return l
+}
+func (rr *PX) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += domainNameLen(rr.Map822, off+l, compression, false)
+       l += domainNameLen(rr.Mapx400, off+l, compression, false)
+       return l
+}
+func (rr *RFC3597) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Rdata)/2 + 1
+       return l
+}
+func (rr *RKEY) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Flags
+       l++    // Protocol
+       l++    // Algorithm
+       l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+       return l
+}
+func (rr *RP) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Mbox, off+l, compression, false)
+       l += domainNameLen(rr.Txt, off+l, compression, false)
+       return l
+}
+func (rr *RRSIG) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // TypeCovered
+       l++    // Algorithm
+       l++    // Labels
+       l += 4 // OrigTtl
+       l += 4 // Expiration
+       l += 4 // Inception
+       l += 2 // KeyTag
+       l += domainNameLen(rr.SignerName, off+l, compression, false)
+       l += base64.StdEncoding.DecodedLen(len(rr.Signature))
+       return l
+}
+func (rr *RT) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Preference
+       l += domainNameLen(rr.Host, off+l, compression, false)
+       return l
+}
+func (rr *SMIMEA) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++ // Usage
+       l++ // Selector
+       l++ // MatchingType
+       l += len(rr.Certificate)/2 + 1
+       return l
+}
+func (rr *SOA) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Ns, off+l, compression, true)
+       l += domainNameLen(rr.Mbox, off+l, compression, true)
+       l += 4 // Serial
+       l += 4 // Refresh
+       l += 4 // Retry
+       l += 4 // Expire
+       l += 4 // Minttl
+       return l
+}
+func (rr *SPF) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       for _, x := range rr.Txt {
+               l += len(x) + 1
+       }
+       return l
+}
+func (rr *SRV) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Priority
+       l += 2 // Weight
+       l += 2 // Port
+       l += domainNameLen(rr.Target, off+l, compression, false)
+       return l
+}
+func (rr *SSHFP) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++ // Algorithm
+       l++ // Type
+       l += len(rr.FingerPrint)/2 + 1
+       return l
+}
+func (rr *TA) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // KeyTag
+       l++    // Algorithm
+       l++    // DigestType
+       l += len(rr.Digest)/2 + 1
+       return l
+}
+func (rr *TALINK) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.PreviousName, off+l, compression, false)
+       l += domainNameLen(rr.NextName, off+l, compression, false)
+       return l
+}
+func (rr *TKEY) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Algorithm, off+l, compression, false)
+       l += 4 // Inception
+       l += 4 // Expiration
+       l += 2 // Mode
+       l += 2 // Error
+       l += 2 // KeySize
+       l += len(rr.Key) / 2
+       l += 2 // OtherLen
+       l += len(rr.OtherData) / 2
+       return l
+}
+func (rr *TLSA) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l++ // Usage
+       l++ // Selector
+       l++ // MatchingType
+       l += len(rr.Certificate)/2 + 1
+       return l
+}
+func (rr *TSIG) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += domainNameLen(rr.Algorithm, off+l, compression, false)
+       l += 6 // TimeSigned
+       l += 2 // Fudge
+       l += 2 // MACSize
+       l += len(rr.MAC) / 2
+       l += 2 // OrigId
+       l += 2 // Error
+       l += 2 // OtherLen
+       l += len(rr.OtherData) / 2
+       return l
+}
+func (rr *TXT) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       for _, x := range rr.Txt {
+               l += len(x) + 1
+       }
+       return l
+}
+func (rr *UID) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 4 // Uid
+       return l
+}
+func (rr *UINFO) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.Uinfo) + 1
+       return l
+}
+func (rr *URI) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += 2 // Priority
+       l += 2 // Weight
+       l += len(rr.Target)
+       return l
+}
+func (rr *X25) len(off int, compression map[string]struct{}) int {
+       l := rr.Hdr.len(off, compression)
+       l += len(rr.PSDNAddress) + 1
+       return l
+}
+
+// copy() functions
+func (rr *A) copy() RR {
+       return &A{rr.Hdr, copyIP(rr.A)}
+}
+func (rr *AAAA) copy() RR {
+       return &AAAA{rr.Hdr, copyIP(rr.AAAA)}
+}
+func (rr *AFSDB) copy() RR {
+       return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname}
+}
+func (rr *ANY) copy() RR {
+       return &ANY{rr.Hdr}
+}
+func (rr *AVC) copy() RR {
+       Txt := make([]string, len(rr.Txt))
+       copy(Txt, rr.Txt)
+       return &AVC{rr.Hdr, Txt}
+}
+func (rr *CAA) copy() RR {
+       return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value}
+}
+func (rr *CERT) copy() RR {
+       return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
+}
+func (rr *CNAME) copy() RR {
+       return &CNAME{rr.Hdr, rr.Target}
+}
+func (rr *CSYNC) copy() RR {
+       TypeBitMap := make([]uint16, len(rr.TypeBitMap))
+       copy(TypeBitMap, rr.TypeBitMap)
+       return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap}
+}
+func (rr *DHCID) copy() RR {
+       return &DHCID{rr.Hdr, rr.Digest}
+}
+func (rr *DNAME) copy() RR {
+       return &DNAME{rr.Hdr, rr.Target}
+}
+func (rr *DNSKEY) copy() RR {
+       return &DNSKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
+}
+func (rr *DS) copy() RR {
+       return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
+}
+func (rr *EID) copy() RR {
+       return &EID{rr.Hdr, rr.Endpoint}
+}
+func (rr *EUI48) copy() RR {
+       return &EUI48{rr.Hdr, rr.Address}
+}
+func (rr *EUI64) copy() RR {
+       return &EUI64{rr.Hdr, rr.Address}
+}
+func (rr *GID) copy() RR {
+       return &GID{rr.Hdr, rr.Gid}
+}
+func (rr *GPOS) copy() RR {
+       return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude}
+}
+func (rr *HINFO) copy() RR {
+       return &HINFO{rr.Hdr, rr.Cpu, rr.Os}
+}
+func (rr *HIP) copy() RR {
+       RendezvousServers := make([]string, len(rr.RendezvousServers))
+       copy(RendezvousServers, rr.RendezvousServers)
+       return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
+}
+func (rr *KX) copy() RR {
+       return &KX{rr.Hdr, rr.Preference, rr.Exchanger}
+}
+func (rr *L32) copy() RR {
+       return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)}
+}
+func (rr *L64) copy() RR {
+       return &L64{rr.Hdr, rr.Preference, rr.Locator64}
+}
+func (rr *LOC) copy() RR {
+       return &LOC{rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
+}
+func (rr *LP) copy() RR {
+       return &LP{rr.Hdr, rr.Preference, rr.Fqdn}
+}
+func (rr *MB) copy() RR {
+       return &MB{rr.Hdr, rr.Mb}
+}
+func (rr *MD) copy() RR {
+       return &MD{rr.Hdr, rr.Md}
+}
+func (rr *MF) copy() RR {
+       return &MF{rr.Hdr, rr.Mf}
+}
+func (rr *MG) copy() RR {
+       return &MG{rr.Hdr, rr.Mg}
+}
+func (rr *MINFO) copy() RR {
+       return &MINFO{rr.Hdr, rr.Rmail, rr.Email}
+}
+func (rr *MR) copy() RR {
+       return &MR{rr.Hdr, rr.Mr}
+}
+func (rr *MX) copy() RR {
+       return &MX{rr.Hdr, rr.Preference, rr.Mx}
+}
+func (rr *NAPTR) copy() RR {
+       return &NAPTR{rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
+}
+func (rr *NID) copy() RR {
+       return &NID{rr.Hdr, rr.Preference, rr.NodeID}
+}
+func (rr *NIMLOC) copy() RR {
+       return &NIMLOC{rr.Hdr, rr.Locator}
+}
+func (rr *NINFO) copy() RR {
+       ZSData := make([]string, len(rr.ZSData))
+       copy(ZSData, rr.ZSData)
+       return &NINFO{rr.Hdr, ZSData}
+}
+func (rr *NS) copy() RR {
+       return &NS{rr.Hdr, rr.Ns}
+}
+func (rr *NSAPPTR) copy() RR {
+       return &NSAPPTR{rr.Hdr, rr.Ptr}
+}
+func (rr *NSEC) copy() RR {
+       TypeBitMap := make([]uint16, len(rr.TypeBitMap))
+       copy(TypeBitMap, rr.TypeBitMap)
+       return &NSEC{rr.Hdr, rr.NextDomain, TypeBitMap}
+}
+func (rr *NSEC3) copy() RR {
+       TypeBitMap := make([]uint16, len(rr.TypeBitMap))
+       copy(TypeBitMap, rr.TypeBitMap)
+       return &NSEC3{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
+}
+func (rr *NSEC3PARAM) copy() RR {
+       return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
+}
+func (rr *NULL) copy() RR {
+       return &NULL{rr.Hdr, rr.Data}
+}
+func (rr *OPENPGPKEY) copy() RR {
+       return &OPENPGPKEY{rr.Hdr, rr.PublicKey}
+}
+func (rr *OPT) copy() RR {
+       Option := make([]EDNS0, len(rr.Option))
+       for i, e := range rr.Option {
+               Option[i] = e.copy()
+       }
+       return &OPT{rr.Hdr, Option}
+}
+func (rr *PTR) copy() RR {
+       return &PTR{rr.Hdr, rr.Ptr}
+}
+func (rr *PX) copy() RR {
+       return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400}
+}
+func (rr *RFC3597) copy() RR {
+       return &RFC3597{rr.Hdr, rr.Rdata}
+}
+func (rr *RKEY) copy() RR {
+       return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
+}
+func (rr *RP) copy() RR {
+       return &RP{rr.Hdr, rr.Mbox, rr.Txt}
+}
+func (rr *RRSIG) copy() RR {
+       return &RRSIG{rr.Hdr, rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
+}
+func (rr *RT) copy() RR {
+       return &RT{rr.Hdr, rr.Preference, rr.Host}
+}
+func (rr *SMIMEA) copy() RR {
+       return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
+}
+func (rr *SOA) copy() RR {
+       return &SOA{rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
+}
+func (rr *SPF) copy() RR {
+       Txt := make([]string, len(rr.Txt))
+       copy(Txt, rr.Txt)
+       return &SPF{rr.Hdr, Txt}
+}
+func (rr *SRV) copy() RR {
+       return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target}
+}
+func (rr *SSHFP) copy() RR {
+       return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint}
+}
+func (rr *TA) copy() RR {
+       return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
+}
+func (rr *TALINK) copy() RR {
+       return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName}
+}
+func (rr *TKEY) copy() RR {
+       return &TKEY{rr.Hdr, rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
+}
+func (rr *TLSA) copy() RR {
+       return &TLSA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
+}
+func (rr *TSIG) copy() RR {
+       return &TSIG{rr.Hdr, rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
+}
+func (rr *TXT) copy() RR {
+       Txt := make([]string, len(rr.Txt))
+       copy(Txt, rr.Txt)
+       return &TXT{rr.Hdr, Txt}
+}
+func (rr *UID) copy() RR {
+       return &UID{rr.Hdr, rr.Uid}
+}
+func (rr *UINFO) copy() RR {
+       return &UINFO{rr.Hdr, rr.Uinfo}
+}
+func (rr *URI) copy() RR {
+       return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target}
+}
+func (rr *X25) copy() RR {
+       return &X25{rr.Hdr, rr.PSDNAddress}
+}