Ver código fonte

Vendoring libnetwork and its dependencies..

- replace /etc/hosts based name resolution with embedded DNS for user
  defined networks
- overlay veth cleanup: docker/docker#18814
- check before programming ipv6 in bridge: docker/docker#19139
- diable DAD: docker/docker#18871

Signed-off-by: Santhosh Manohar <santhosh@docker.com>
Santhosh Manohar 9 anos atrás
pai
commit
8ccf5cffa7
79 arquivos alterados com 15590 adições e 215 exclusões
  1. 3 2
      hack/vendor.sh
  2. 5 0
      vendor/src/github.com/docker/libnetwork/Dockerfile.build
  3. 9 9
      vendor/src/github.com/docker/libnetwork/Makefile
  4. 2 2
      vendor/src/github.com/docker/libnetwork/controller.go
  5. 1 4
      vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go
  6. 16 0
      vendor/src/github.com/docker/libnetwork/drivers/bridge/interface.go
  7. 12 0
      vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go
  8. 11 18
      vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go
  9. 10 5
      vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify.go
  10. 11 0
      vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go
  11. 4 3
      vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go
  12. 70 17
      vendor/src/github.com/docker/libnetwork/endpoint.go
  13. 8 0
      vendor/src/github.com/docker/libnetwork/iptables/iptables.go
  14. 60 0
      vendor/src/github.com/docker/libnetwork/netutils/utils.go
  15. 68 59
      vendor/src/github.com/docker/libnetwork/network.go
  16. 2 1
      vendor/src/github.com/docker/libnetwork/osl/interface_linux.go
  17. 205 0
      vendor/src/github.com/docker/libnetwork/resolver.go
  18. 174 6
      vendor/src/github.com/docker/libnetwork/sandbox.go
  19. 4 0
      vendor/src/github.com/miekg/dns/.gitignore
  20. 7 0
      vendor/src/github.com/miekg/dns/.travis.yml
  21. 1 0
      vendor/src/github.com/miekg/dns/AUTHORS
  22. 9 0
      vendor/src/github.com/miekg/dns/CONTRIBUTORS
  23. 9 0
      vendor/src/github.com/miekg/dns/COPYRIGHT
  24. 32 0
      vendor/src/github.com/miekg/dns/LICENSE
  25. 153 0
      vendor/src/github.com/miekg/dns/README.md
  26. 385 0
      vendor/src/github.com/miekg/dns/client.go
  27. 99 0
      vendor/src/github.com/miekg/dns/clientconfig.go
  28. 278 0
      vendor/src/github.com/miekg/dns/defaults.go
  29. 100 0
      vendor/src/github.com/miekg/dns/dns.go
  30. 664 0
      vendor/src/github.com/miekg/dns/dnssec.go
  31. 156 0
      vendor/src/github.com/miekg/dns/dnssec_keygen.go
  32. 249 0
      vendor/src/github.com/miekg/dns/dnssec_keyscan.go
  33. 85 0
      vendor/src/github.com/miekg/dns/dnssec_privkey.go
  34. 251 0
      vendor/src/github.com/miekg/dns/doc.go
  35. 505 0
      vendor/src/github.com/miekg/dns/edns.go
  36. 96 0
      vendor/src/github.com/miekg/dns/format.go
  37. 162 0
      vendor/src/github.com/miekg/dns/labels.go
  38. 1945 0
      vendor/src/github.com/miekg/dns/msg.go
  39. 112 0
      vendor/src/github.com/miekg/dns/nsecx.go
  40. 117 0
      vendor/src/github.com/miekg/dns/privaterr.go
  41. 95 0
      vendor/src/github.com/miekg/dns/rawmsg.go
  42. 84 0
      vendor/src/github.com/miekg/dns/sanitize.go
  43. 43 0
      vendor/src/github.com/miekg/dns/scanner.go
  44. 687 0
      vendor/src/github.com/miekg/dns/server.go
  45. 216 0
      vendor/src/github.com/miekg/dns/sig0.go
  46. 57 0
      vendor/src/github.com/miekg/dns/singleinflight.go
  47. 86 0
      vendor/src/github.com/miekg/dns/tlsa.go
  48. 320 0
      vendor/src/github.com/miekg/dns/tsig.go
  49. 1328 0
      vendor/src/github.com/miekg/dns/types.go
  50. 266 0
      vendor/src/github.com/miekg/dns/types_generate.go
  51. 58 0
      vendor/src/github.com/miekg/dns/udp.go
  52. 63 0
      vendor/src/github.com/miekg/dns/udp_linux.go
  53. 17 0
      vendor/src/github.com/miekg/dns/udp_other.go
  54. 34 0
      vendor/src/github.com/miekg/dns/udp_windows.go
  55. 94 0
      vendor/src/github.com/miekg/dns/update.go
  56. 244 0
      vendor/src/github.com/miekg/dns/xfr.go
  57. 158 0
      vendor/src/github.com/miekg/dns/zgenerate.go
  58. 974 0
      vendor/src/github.com/miekg/dns/zscan.go
  59. 2270 0
      vendor/src/github.com/miekg/dns/zscan_rr.go
  60. 842 0
      vendor/src/github.com/miekg/dns/ztypes.go
  61. 6 1
      vendor/src/github.com/vishvananda/netlink/.travis.yml
  62. 1 1
      vendor/src/github.com/vishvananda/netlink/Makefile
  63. 3 1
      vendor/src/github.com/vishvananda/netlink/addr.go
  64. 14 0
      vendor/src/github.com/vishvananda/netlink/addr_linux.go
  65. 39 15
      vendor/src/github.com/vishvananda/netlink/class_linux.go
  66. 312 0
      vendor/src/github.com/vishvananda/netlink/link.go
  67. 330 0
      vendor/src/github.com/vishvananda/netlink/link_linux.go
  68. 3 2
      vendor/src/github.com/vishvananda/netlink/neigh_linux.go
  69. 5 2
      vendor/src/github.com/vishvananda/netlink/netlink.go
  70. 80 0
      vendor/src/github.com/vishvananda/netlink/nl/link_linux.go
  71. 9 3
      vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go
  72. 37 0
      vendor/src/github.com/vishvananda/netlink/nl/syscall.go
  73. 130 11
      vendor/src/github.com/vishvananda/netlink/nl/tc_linux.go
  74. 123 0
      vendor/src/github.com/vishvananda/netlink/qdisc.go
  75. 114 15
      vendor/src/github.com/vishvananda/netlink/qdisc_linux.go
  76. 13 10
      vendor/src/github.com/vishvananda/netlink/route.go
  77. 104 28
      vendor/src/github.com/vishvananda/netlink/route_linux.go
  78. 43 0
      vendor/src/github.com/vishvananda/netlink/rule.go
  79. 198 0
      vendor/src/github.com/vishvananda/netlink/rule_linux.go

+ 3 - 2
hack/vendor.sh

@@ -25,14 +25,14 @@ clone git github.com/docker/go-connections v0.1.2
 clone git github.com/docker/engine-api v0.1.3
 
 #get libnetwork packages
-clone git github.com/docker/libnetwork 49c24217054e269aad3dbfd81ee32780b104dd84
+clone git github.com/docker/libnetwork c8ec4bd24e1e76feb4f79e3924c68cd2ce89938a
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
 clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
 clone git github.com/hashicorp/serf 7151adcef72687bf95f451a2e0ba15cb19412bf2
 clone git github.com/docker/libkv c2aac5dbbaa5c872211edea7c0f32b3bd67e7410
 clone git github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
-clone git github.com/vishvananda/netlink 8e810149a2e531fed9b837c0c7d8a8922d2bedf7
+clone git github.com/vishvananda/netlink bfd70f556483c008636b920dda142fdaa0d59ef9
 clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
 clone git github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
 clone git github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
@@ -41,6 +41,7 @@ fix_rewritten_imports github.com/coreos/etcd
 clone git github.com/ugorji/go 5abd4e96a45c386928ed2ca2a7ef63e2533e18ec
 clone git github.com/hashicorp/consul v0.5.2
 clone git github.com/boltdb/bolt v1.1.0
+clone git github.com/miekg/dns d27455715200c7d3e321a1e5cadb27c9ee0b0f02
 
 # get graph and distribution packages
 clone git github.com/docker/distribution 568bf038af6d65b376165d02886b1c7fcaef1f61

+ 5 - 0
vendor/src/github.com/docker/libnetwork/Dockerfile.build

@@ -1,5 +1,10 @@
 FROM golang:1.4-cross
 RUN apt-get update && apt-get -y install iptables
+
+RUN cd /go/src && mkdir -p golang.org/x && \
+    cd golang.org/x && git clone https://github.com/golang/tools && \
+    cd tools && git checkout release-branch.go1.5
+
 RUN go get github.com/tools/godep \
 		github.com/golang/lint/golint \
 		golang.org/x/tools/cmd/vet \

+ 9 - 9
vendor/src/github.com/docker/libnetwork/Makefile

@@ -4,18 +4,18 @@ build_image=libnetworkbuild
 dockerargs = --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork
 container_env = -e "INSIDECONTAINER=-incontainer=true"
 docker = docker run --rm -it ${dockerargs} $$EXTRA_ARGS ${container_env} ${build_image}
-ciargs = -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true"
+ciargs = -e CIRCLECI -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true"
 cidocker = docker run ${dockerargs} ${ciargs} ${container_env} ${build_image}
 CROSS_PLATFORMS = linux/amd64 linux/386 linux/arm windows/amd64 windows/386
 
-${build_image}.created:
-	docker build -f Dockerfile.build -t ${build_image} .
-	touch ${build_image}.created
-
 all: ${build_image}.created build check integration-tests clean
 
 all-local: build-local check-local integration-tests-local clean
 
+${build_image}.created:
+	docker build -f Dockerfile.build -t ${build_image} .
+	touch ${build_image}.created
+
 build: ${build_image}.created
 	@echo "Building code... "
 	@${docker} ./wrapmake.sh build-local
@@ -34,9 +34,9 @@ clean:
 cross: ${build_image}.created
 	@mkdir -p "bin"
 	@for platform in ${CROSS_PLATFORMS}; do \
-	        EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
+		EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
 		echo "$${platform}..." ; \
-	        ${docker} make cross-local ; \
+		${docker} make cross-local ; \
 	done
 
 cross-local:
@@ -91,9 +91,9 @@ coveralls:
 circle-ci-cross: ${build_image}.created
 	@mkdir -p "bin"
 	@for platform in ${CROSS_PLATFORMS}; do \
-	        EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
+		EXTRA_ARGS="-e GOOS=$${platform%/*} -e GOARCH=$${platform##*/}" ; \
 		echo "$${platform}..." ; \
-	        ${cidocker} make cross-local ; \
+		${cidocker} make cross-local ; \
 	done
 
 circle-ci-check: ${build_image}.created

+ 2 - 2
vendor/src/github.com/docker/libnetwork/controller.go

@@ -143,7 +143,7 @@ type controller struct {
 	extKeyListener net.Listener
 	watchCh        chan *endpoint
 	unWatchCh      chan *endpoint
-	svcDb          map[string]svcMap
+	svcDb          map[string]svcInfo
 	nmap           map[string]*netWatch
 	defOsSbox      osl.Sandbox
 	sboxOnce       sync.Once
@@ -171,7 +171,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
 		sandboxes:   sandboxTable{},
 		drivers:     driverTable{},
 		ipamDrivers: ipamTable{},
-		svcDb:       make(map[string]svcMap),
+		svcDb:       make(map[string]svcInfo),
 	}
 
 	if err := c.initStores(); err != nil {

+ 1 - 4
vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go

@@ -134,10 +134,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
 	if err := iptables.FirewalldInit(); err != nil {
 		logrus.Debugf("Fail to initialize firewalld: %v, using raw iptables instead", err)
 	}
-	if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
-		logrus.Warnf("Failed to remove existing iptables entries in %s : %v", DockerChain, err)
-	}
-
+	removeIPChains()
 	d := newDriver()
 	if err := d.configure(config); err != nil {
 		return err

+ 16 - 0
vendor/src/github.com/docker/libnetwork/drivers/bridge/interface.go

@@ -1,6 +1,7 @@
 package bridge
 
 import (
+	"fmt"
 	"net"
 
 	"github.com/vishvananda/netlink"
@@ -61,3 +62,18 @@ func (i *bridgeInterface) addresses() (netlink.Addr, []netlink.Addr, error) {
 	}
 	return v4addr[0], v6addr, nil
 }
+
+func (i *bridgeInterface) programIPv6Address() error {
+	_, nlAddressList, err := i.addresses()
+	if err != nil {
+		return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: fmt.Errorf("failed to retrieve address list: %v", err)}
+	}
+	nlAddr := netlink.Addr{IPNet: i.bridgeIPv6}
+	if findIPv6Address(nlAddr, nlAddressList) {
+		return nil
+	}
+	if err := netlink.AddrAdd(i.Link, &nlAddr); err != nil {
+		return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err}
+	}
+	return nil
+}

+ 12 - 0
vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go

@@ -309,3 +309,15 @@ func ensureJumpRule(fromChain, toChain string) error {
 
 	return nil
 }
+
+func removeIPChains() {
+	for _, chainInfo := range []iptables.ChainInfo{
+		iptables.ChainInfo{Name: DockerChain, Table: iptables.Nat},
+		iptables.ChainInfo{Name: DockerChain, Table: iptables.Filter},
+		iptables.ChainInfo{Name: IsolationChain, Table: iptables.Filter},
+	} {
+		if err := chainInfo.Remove(); err != nil {
+			logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
+		}
+	}
+}

+ 11 - 18
vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go

@@ -7,6 +7,7 @@ import (
 	"os"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
 )
 
@@ -22,9 +23,8 @@ const (
 func init() {
 	// We allow ourselves to panic in this special case because we indicate a
 	// failure to parse a compile-time define constant.
-	if ip, netw, err := net.ParseCIDR(bridgeIPv6Str); err == nil {
-		bridgeIPv6 = &net.IPNet{IP: ip, Mask: netw.Mask}
-	} else {
+	var err error
+	if bridgeIPv6, err = types.ParseCIDR(bridgeIPv6Str); err != nil {
 		panic(fmt.Sprintf("Cannot parse default bridge IPv6 address %q: %v", bridgeIPv6Str, err))
 	}
 }
@@ -42,31 +42,24 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error {
 		}
 	}
 
-	_, addrsv6, err := i.addresses()
-	if err != nil {
-		return err
-	}
-
-	// Add the default link local ipv6 address if it doesn't exist
-	if !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) {
-		if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
-			return &IPv6AddrAddError{IP: bridgeIPv6, Err: err}
-		}
-	}
-
 	// Store bridge network and default gateway
 	i.bridgeIPv6 = bridgeIPv6
 	i.gatewayIPv6 = i.bridgeIPv6.IP
 
+	if err := i.programIPv6Address(); err != nil {
+		return err
+	}
+
 	if config.AddressIPv6 == nil {
 		return nil
 	}
 
-	// Store and program user specified bridge network and network gateway
+	// Store the user specified bridge network and network gateway and program it
 	i.bridgeIPv6 = config.AddressIPv6
 	i.gatewayIPv6 = config.AddressIPv6.IP
-	if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: i.bridgeIPv6}); err != nil {
-		return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err}
+
+	if err := i.programIPv6Address(); err != nil {
+		return err
 	}
 
 	// Setting route to global IPv6 subnet

+ 10 - 5
vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_verify.go

@@ -1,6 +1,8 @@
 package bridge
 
 import (
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
 )
 
@@ -27,11 +29,14 @@ func setupVerifyAndReconcile(config *networkConfiguration, i *bridgeInterface) e
 		return (*IPv6AddrNoMatchError)(bridgeIPv6)
 	}
 
-	// By this time we have either configured a new bridge with an IP address
-	// or made sure an existing bridge's IP matches the configuration
-	// Now is the time to cache these states in the bridgeInterface.
-	i.bridgeIPv4 = addrv4.IPNet
-	i.bridgeIPv6 = bridgeIPv6
+	// Release any residual IPv6 address that might be there because of older daemon instances
+	for _, addrv6 := range addrsv6 {
+		if addrv6.IP.IsGlobalUnicast() && !types.CompareIPNet(addrv6.IPNet, i.bridgeIPv6) {
+			if err := netlink.AddrDel(i.Link, &addrv6); err != nil {
+				log.Warnf("Failed to remove residual IPv6 address %s from bridge: %v", addrv6.IPNet, err)
+			}
+		}
+	}
 
 	return nil
 }

+ 11 - 0
vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go

@@ -54,6 +54,8 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 		return err
 	}
 
+	ep.ifName = name2
+
 	// Set the container interface and its peer MTU to 1450 to allow
 	// for 50 bytes vxlan encap (inner eth header(14) + outer IP(20) +
 	// outer UDP(8) + vxlan header(8))
@@ -134,5 +136,14 @@ func (d *driver) Leave(nid, eid string) error {
 
 	n.leaveSandbox()
 
+	link, err := netlink.LinkByName(ep.ifName)
+	if err != nil {
+		log.Warnf("Failed to retrieve interface link for interface removal on endpoint leave: %v", err)
+		return nil
+	}
+	if err := netlink.LinkDel(link); err != nil {
+		log.Warnf("Failed to delete interface link on endpoint leave: %v", err)
+	}
+
 	return nil
 }

+ 4 - 3
vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go

@@ -11,9 +11,10 @@ import (
 type endpointTable map[string]*endpoint
 
 type endpoint struct {
-	id   string
-	mac  net.HardwareAddr
-	addr *net.IPNet
+	id     string
+	ifName string
+	mac    net.HardwareAddr
+	addr   *net.IPNet
 }
 
 func (n *network) endpoint(eid string) *endpoint {

+ 70 - 17
vendor/src/github.com/docker/libnetwork/endpoint.go

@@ -50,21 +50,24 @@ type Endpoint interface {
 type EndpointOption func(ep *endpoint)
 
 type endpoint struct {
-	name          string
-	id            string
-	network       *network
-	iface         *endpointInterface
-	joinInfo      *endpointJoinInfo
-	sandboxID     string
-	exposedPorts  []types.TransportPort
-	anonymous     bool
-	generic       map[string]interface{}
-	joinLeaveDone chan struct{}
-	prefAddress   net.IP
-	prefAddressV6 net.IP
-	ipamOptions   map[string]string
-	dbIndex       uint64
-	dbExists      bool
+	name              string
+	id                string
+	network           *network
+	iface             *endpointInterface
+	joinInfo          *endpointJoinInfo
+	sandboxID         string
+	exposedPorts      []types.TransportPort
+	anonymous         bool
+	disableResolution bool
+	generic           map[string]interface{}
+	joinLeaveDone     chan struct{}
+	prefAddress       net.IP
+	prefAddressV6     net.IP
+	ipamOptions       map[string]string
+	aliases           map[string]string
+	myAliases         []string
+	dbIndex           uint64
+	dbExists          bool
 	sync.Mutex
 }
 
@@ -82,6 +85,8 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
 	}
 	epMap["sandbox"] = ep.sandboxID
 	epMap["anonymous"] = ep.anonymous
+	epMap["disableResolution"] = ep.disableResolution
+	epMap["myAliases"] = ep.myAliases
 	return json.Marshal(epMap)
 }
 
@@ -159,6 +164,13 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
 	if v, ok := epMap["anonymous"]; ok {
 		ep.anonymous = v.(bool)
 	}
+	if v, ok := epMap["disableResolution"]; ok {
+		ep.disableResolution = v.(bool)
+	}
+	ma, _ := json.Marshal(epMap["myAliases"])
+	var myAliases []string
+	json.Unmarshal(ma, &myAliases)
+	ep.myAliases = myAliases
 	return nil
 }
 
@@ -177,6 +189,7 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error {
 	dstEp.dbIndex = ep.dbIndex
 	dstEp.dbExists = ep.dbExists
 	dstEp.anonymous = ep.anonymous
+	dstEp.disableResolution = ep.disableResolution
 
 	if ep.iface != nil {
 		dstEp.iface = &endpointInterface{}
@@ -186,6 +199,9 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error {
 	dstEp.exposedPorts = make([]types.TransportPort, len(ep.exposedPorts))
 	copy(dstEp.exposedPorts, ep.exposedPorts)
 
+	dstEp.myAliases = make([]string, len(ep.myAliases))
+	copy(dstEp.myAliases, ep.myAliases)
+
 	dstEp.generic = options.Generic{}
 	for k, v := range ep.generic {
 		dstEp.generic[k] = v
@@ -208,6 +224,13 @@ func (ep *endpoint) Name() string {
 	return ep.name
 }
 
+func (ep *endpoint) MyAliases() []string {
+	ep.Lock()
+	defer ep.Unlock()
+
+	return ep.myAliases
+}
+
 func (ep *endpoint) Network() string {
 	if ep.network == nil {
 		return ""
@@ -222,6 +245,12 @@ func (ep *endpoint) isAnonymous() bool {
 	return ep.anonymous
 }
 
+func (ep *endpoint) needResolver() bool {
+	ep.Lock()
+	defer ep.Unlock()
+	return !ep.disableResolution
+}
+
 // endpoint Key structure : endpoint/network-id/endpoint-id
 func (ep *endpoint) Key() []string {
 	if ep.network == nil {
@@ -396,10 +425,9 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
 	if ip := ep.getFirstInterfaceAddress(); ip != nil {
 		address = ip.String()
 	}
-	if err = sb.updateHostsFile(address, network.getSvcRecords(ep)); err != nil {
+	if err = sb.updateHostsFile(address); err != nil {
 		return err
 	}
-
 	if err = sb.updateDNS(network.enableIPv6); err != nil {
 		return err
 	}
@@ -729,6 +757,31 @@ func CreateOptionAnonymous() EndpointOption {
 	}
 }
 
+// CreateOptionDisableResolution function returns an option setter to indicate
+// this endpoint doesn't want embedded DNS server functionality
+func CreateOptionDisableResolution() EndpointOption {
+	return func(ep *endpoint) {
+		ep.disableResolution = true
+	}
+}
+
+//CreateOptionAlias function returns an option setter for setting endpoint alias
+func CreateOptionAlias(name string, alias string) EndpointOption {
+	return func(ep *endpoint) {
+		if ep.aliases == nil {
+			ep.aliases = make(map[string]string)
+		}
+		ep.aliases[alias] = name
+	}
+}
+
+//CreateOptionMyAlias function returns an option setter for setting endpoint's self alias
+func CreateOptionMyAlias(alias string) EndpointOption {
+	return func(ep *endpoint) {
+		ep.myAliases = append(ep.myAliases, alias)
+	}
+}
+
 // JoinOptionPriority function returns an option setter for priority option to
 // be passed to the endpoint.Join() method.
 func JoinOptionPriority(ep Endpoint, prio int) EndpointOption {

+ 8 - 0
vendor/src/github.com/docker/libnetwork/iptables/iptables.go

@@ -361,3 +361,11 @@ func RawCombinedOutput(args ...string) error {
 	}
 	return nil
 }
+
+// ExistChain checks if a chain exists
+func ExistChain(chain string, table Table) bool {
+	if _, err := Raw("-t", string(table), "-L", chain); err == nil {
+		return true
+	}
+	return false
+}

+ 60 - 0
vendor/src/github.com/docker/libnetwork/netutils/utils.go

@@ -9,6 +9,7 @@ import (
 	"fmt"
 	"io"
 	"net"
+	"strings"
 
 	"github.com/docker/libnetwork/types"
 )
@@ -132,3 +133,62 @@ func GenerateRandomName(prefix string, size int) (string, error) {
 	}
 	return prefix + hex.EncodeToString(id)[:size], nil
 }
+
+// ReverseIP accepts a V4 or V6 IP string in the canonical form and returns a reversed IP in
+// the dotted decimal form . This is used to setup the IP to service name mapping in the optimal
+// way for the DNS PTR queries.
+func ReverseIP(IP string) string {
+	var reverseIP []string
+
+	if net.ParseIP(IP).To4() != nil {
+		reverseIP = strings.Split(IP, ".")
+		l := len(reverseIP)
+		for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
+			reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i]
+		}
+	} else {
+		reverseIP = strings.Split(IP, ":")
+
+		// Reversed IPv6 is represented in dotted decimal instead of the typical
+		// colon hex notation
+		for key := range reverseIP {
+			if len(reverseIP[key]) == 0 { // expand the compressed 0s
+				reverseIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":"))
+			} else if len(reverseIP[key]) < 4 { // 0-padding needed
+				reverseIP[key] = strings.Repeat("0", 4-len(reverseIP[key])) + reverseIP[key]
+			}
+		}
+
+		reverseIP = strings.Split(strings.Join(reverseIP, ""), "")
+
+		l := len(reverseIP)
+		for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
+			reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i]
+		}
+	}
+
+	return strings.Join(reverseIP, ".")
+}
+
+// ParseAlias parses and validates the specified string as a alias format (name:alias)
+func ParseAlias(val string) (string, string, error) {
+	if val == "" {
+		return "", "", fmt.Errorf("empty string specified for alias")
+	}
+	arr := strings.Split(val, ":")
+	if len(arr) > 2 {
+		return "", "", fmt.Errorf("bad format for alias: %s", val)
+	}
+	if len(arr) == 1 {
+		return val, val, nil
+	}
+	return arr[0], arr[1], nil
+}
+
+// ValidateAlias validates that the specified string has a valid alias format (containerName:alias).
+func ValidateAlias(val string) (string, error) {
+	if _, _, err := ParseAlias(val); err != nil {
+		return val, err
+	}
+	return val, nil
+}

+ 68 - 59
vendor/src/github.com/docker/libnetwork/network.go

@@ -58,17 +58,21 @@ type Network interface {
 
 // NetworkInfo returns some configuration and operational information about the network
 type NetworkInfo interface {
-	IpamConfig() (string, []*IpamConf, []*IpamConf)
+	IpamConfig() (string, map[string]string, []*IpamConf, []*IpamConf)
 	IpamInfo() ([]*IpamInfo, []*IpamInfo)
 	DriverOptions() map[string]string
 	Scope() string
+	Internal() bool
 }
 
 // EndpointWalker is a client provided function which will be used to walk the Endpoints.
 // When the function returns true, the walk will stop.
 type EndpointWalker func(ep Endpoint) bool
 
-type svcMap map[string]net.IP
+type svcInfo struct {
+	svcMap map[string][]net.IP
+	ipMap  map[string]string
+}
 
 // IpamConf contains all the ipam related configurations for a network
 type IpamConf struct {
@@ -77,8 +81,6 @@ type IpamConf struct {
 	// A subset of the master pool. If specified,
 	// this becomes the container pool
 	SubPool string
-	// Input options for IPAM Driver (optional)
-	Options map[string]string
 	// Preferred Network Gateway address (optional)
 	Gateway string
 	// Auxiliary addresses for network driver. Must be within the master pool.
@@ -148,6 +150,7 @@ type network struct {
 	networkType  string
 	id           string
 	ipamType     string
+	ipamOptions  map[string]string
 	addrSpace    string
 	ipamV4Config []*IpamConf
 	ipamV6Config []*IpamConf
@@ -158,7 +161,6 @@ type network struct {
 	epCnt        *endpointCnt
 	generic      options.Generic
 	dbIndex      uint64
-	svcRecords   svcMap
 	dbExists     bool
 	persist      bool
 	stopWatchCh  chan struct{}
@@ -252,12 +254,6 @@ func (c *IpamConf) CopyTo(dstC *IpamConf) error {
 	dstC.PreferredPool = c.PreferredPool
 	dstC.SubPool = c.SubPool
 	dstC.Gateway = c.Gateway
-	if c.Options != nil {
-		dstC.Options = make(map[string]string, len(c.Options))
-		for k, v := range c.Options {
-			dstC.Options[k] = v
-		}
-	}
 	if c.AuxAddresses != nil {
 		dstC.AuxAddresses = make(map[string]string, len(c.AuxAddresses))
 		for k, v := range c.AuxAddresses {
@@ -499,11 +495,12 @@ func NetworkOptionInternalNetwork() NetworkOption {
 }
 
 // NetworkOptionIpam function returns an option setter for the ipam configuration for this network
-func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf) NetworkOption {
+func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf, opts map[string]string) NetworkOption {
 	return func(n *network) {
 		if ipamDriver != "" {
 			n.ipamType = ipamDriver
 		}
+		n.ipamOptions = opts
 		n.addrSpace = addrSpace
 		n.ipamV4Config = ipV4
 		n.ipamV6Config = ipV6
@@ -828,64 +825,76 @@ func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool
 		return
 	}
 
-	c := n.getController()
-	sr, ok := c.svcDb[n.ID()]
-	if !ok {
-		c.svcDb[n.ID()] = svcMap{}
-		sr = c.svcDb[n.ID()]
-	}
-
-	n.Lock()
-	var recs []etchosts.Record
+	epName := ep.Name()
 	if iface := ep.Iface(); iface.Address() != nil {
+		myAliases := ep.MyAliases()
 		if isAdd {
-			// If we already have this endpoint in service db just return
-			if _, ok := sr[ep.Name()]; ok {
-				n.Unlock()
-				return
+			n.addSvcRecords(epName, iface.Address().IP, true)
+			for _, alias := range myAliases {
+				n.addSvcRecords(alias, iface.Address().IP, false)
 			}
-
-			sr[ep.Name()] = iface.Address().IP
-			sr[ep.Name()+"."+n.name] = iface.Address().IP
 		} else {
-			delete(sr, ep.Name())
-			delete(sr, ep.Name()+"."+n.name)
+			n.deleteSvcRecords(epName, iface.Address().IP, true)
+			for _, alias := range myAliases {
+				n.deleteSvcRecords(alias, iface.Address().IP, false)
+			}
 		}
+	}
+}
 
-		recs = append(recs, etchosts.Record{
-			Hosts: ep.Name(),
-			IP:    iface.Address().IP.String(),
-		})
+func (n *network) addSvcRecords(name string, epIP net.IP, ipMapUpdate bool) {
+	c := n.getController()
+	c.Lock()
+	defer c.Unlock()
+	sr, ok := c.svcDb[n.ID()]
+	if !ok {
+		sr = svcInfo{
+			svcMap: make(map[string][]net.IP),
+			ipMap:  make(map[string]string),
+		}
+		c.svcDb[n.ID()] = sr
+	}
 
-		recs = append(recs, etchosts.Record{
-			Hosts: ep.Name() + "." + n.name,
-			IP:    iface.Address().IP.String(),
-		})
+	if ipMapUpdate {
+		reverseIP := netutils.ReverseIP(epIP.String())
+		if _, ok := sr.ipMap[reverseIP]; !ok {
+			sr.ipMap[reverseIP] = name
+		}
 	}
-	n.Unlock()
 
-	// If there are no records to add or delete then simply return here
-	if len(recs) == 0 {
+	ipList := sr.svcMap[name]
+	for _, ip := range ipList {
+		if ip.Equal(epIP) {
+			return
+		}
+	}
+	sr.svcMap[name] = append(sr.svcMap[name], epIP)
+}
+
+func (n *network) deleteSvcRecords(name string, epIP net.IP, ipMapUpdate bool) {
+	c := n.getController()
+	c.Lock()
+	defer c.Unlock()
+	sr, ok := c.svcDb[n.ID()]
+	if !ok {
 		return
 	}
 
-	var sbList []*sandbox
-	for _, lEp := range localEps {
-		if ep.ID() == lEp.ID() {
-			continue
-		}
+	if ipMapUpdate {
+		delete(sr.ipMap, netutils.ReverseIP(epIP.String()))
+	}
 
-		if sb, hasSandbox := lEp.getSandbox(); hasSandbox {
-			sbList = append(sbList, sb)
+	ipList := sr.svcMap[name]
+	for i, ip := range ipList {
+		if ip.Equal(epIP) {
+			ipList = append(ipList[:i], ipList[i+1:]...)
+			break
 		}
 	}
+	sr.svcMap[name] = ipList
 
-	for _, sb := range sbList {
-		if isAdd {
-			sb.addHostsEntries(recs)
-		} else {
-			sb.deleteHostsEntries(recs)
-		}
+	if len(ipList) == 0 {
+		delete(sr.svcMap, name)
 	}
 }
 
@@ -896,14 +905,14 @@ func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
 	var recs []etchosts.Record
 	sr, _ := n.ctrlr.svcDb[n.id]
 
-	for h, ip := range sr {
+	for h, ip := range sr.svcMap {
 		if ep != nil && strings.Split(h, ".")[0] == ep.Name() {
 			continue
 		}
 
 		recs = append(recs, etchosts.Record{
 			Hosts: h,
-			IP:    ip.String(),
+			IP:    ip[0].String(),
 		})
 	}
 
@@ -983,7 +992,7 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
 		d := &IpamInfo{}
 		(*infoList)[i] = d
 
-		d.PoolID, d.Pool, d.Meta, err = ipam.RequestPool(n.addrSpace, cfg.PreferredPool, cfg.SubPool, cfg.Options, ipVer == 6)
+		d.PoolID, d.Pool, d.Meta, err = ipam.RequestPool(n.addrSpace, cfg.PreferredPool, cfg.SubPool, n.ipamOptions, ipVer == 6)
 		if err != nil {
 			return err
 		}
@@ -1162,7 +1171,7 @@ func (n *network) Scope() string {
 	return n.driverScope()
 }
 
-func (n *network) IpamConfig() (string, []*IpamConf, []*IpamConf) {
+func (n *network) IpamConfig() (string, map[string]string, []*IpamConf, []*IpamConf) {
 	n.Lock()
 	defer n.Unlock()
 
@@ -1181,7 +1190,7 @@ func (n *network) IpamConfig() (string, []*IpamConf, []*IpamConf) {
 		v6L[i] = cc
 	}
 
-	return n.ipamType, v4L, v6L
+	return n.ipamType, n.ipamOptions, v4L, v6L
 }
 
 func (n *network) IpamInfo() ([]*IpamInfo, []*IpamInfo) {

+ 2 - 1
vendor/src/github.com/docker/libnetwork/osl/interface_linux.go

@@ -6,6 +6,7 @@ import (
 	"os/exec"
 	"regexp"
 	"sync"
+	"syscall"
 
 	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
@@ -337,7 +338,7 @@ func setInterfaceIPv6(iface netlink.Link, i *nwIface) error {
 	if i.AddressIPv6() == nil {
 		return nil
 	}
-	ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: ""}
+	ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD}
 	return netlink.AddrAdd(iface, ipAddr)
 }
 

+ 205 - 0
vendor/src/github.com/docker/libnetwork/resolver.go

@@ -0,0 +1,205 @@
+package libnetwork
+
+import (
+	"fmt"
+	"net"
+	"strings"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/iptables"
+	"github.com/miekg/dns"
+)
+
+// Resolver represents the embedded DNS server in Docker. It operates
+// by listening on container's loopback interface for DNS queries.
+type Resolver interface {
+	// Start starts the name server for the container
+	Start() error
+	// Stop stops the name server for the container
+	Stop()
+	// SetupFunc() provides the setup function that should be run
+	// in the container's network namespace.
+	SetupFunc() func()
+	// NameServer() returns the IP of the DNS resolver for the
+	// containers.
+	NameServer() string
+	// To configure external name servers the resolver should use
+	SetExtServers([]string)
+	// ResolverOptions returns resolv.conf options that should be set
+	ResolverOptions() []string
+}
+
+const (
+	resolverIP    = "127.0.0.11"
+	dnsPort       = "53"
+	ptrIPv4domain = ".in-addr.arpa."
+	ptrIPv6domain = ".ip6.arpa."
+	respTTL       = 1800
+)
+
+// resolver implements the Resolver interface
+type resolver struct {
+	sb     *sandbox
+	extDNS []string
+	server *dns.Server
+	conn   *net.UDPConn
+	err    error
+}
+
+// NewResolver creates a new instance of the Resolver
+func NewResolver(sb *sandbox) Resolver {
+	return &resolver{
+		sb:  sb,
+		err: fmt.Errorf("setup not done yet"),
+	}
+}
+
+func (r *resolver) SetupFunc() func() {
+	return (func() {
+		var err error
+
+		addr := &net.UDPAddr{
+			IP: net.ParseIP(resolverIP),
+		}
+
+		r.conn, err = net.ListenUDP("udp", addr)
+		if err != nil {
+			r.err = fmt.Errorf("error in opening name server socket %v", err)
+			return
+		}
+		laddr := r.conn.LocalAddr()
+		_, ipPort, _ := net.SplitHostPort(laddr.String())
+
+		rules := [][]string{
+			{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
+			{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
+		}
+
+		for _, rule := range rules {
+			r.err = iptables.RawCombinedOutput(rule...)
+			if r.err != nil {
+				return
+			}
+		}
+		r.err = nil
+	})
+}
+
+func (r *resolver) Start() error {
+	// make sure the resolver has been setup before starting
+	if r.err != nil {
+		return r.err
+	}
+	s := &dns.Server{Handler: r, PacketConn: r.conn}
+	r.server = s
+	go func() {
+		s.ActivateAndServe()
+	}()
+	return nil
+}
+
+func (r *resolver) Stop() {
+	if r.server != nil {
+		r.server.Shutdown()
+	}
+}
+
+func (r *resolver) SetExtServers(dns []string) {
+	r.extDNS = dns
+}
+
+func (r *resolver) NameServer() string {
+	return resolverIP
+}
+
+func (r *resolver) ResolverOptions() []string {
+	return []string{"ndots:0"}
+}
+
+func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
+	addr := r.sb.ResolveName(name)
+	if addr == nil {
+		return nil, nil
+	}
+
+	log.Debugf("Lookup for %s: IP %s", name, addr.String())
+
+	resp := new(dns.Msg)
+	resp.SetReply(query)
+
+	rr := new(dns.A)
+	rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
+	rr.A = addr
+	resp.Answer = append(resp.Answer, rr)
+	return resp, nil
+}
+
+func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
+	parts := []string{}
+
+	if strings.HasSuffix(ptr, ptrIPv4domain) {
+		parts = strings.Split(ptr, ptrIPv4domain)
+	} else if strings.HasSuffix(ptr, ptrIPv6domain) {
+		parts = strings.Split(ptr, ptrIPv6domain)
+	} else {
+		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
+	}
+
+	host := r.sb.ResolveIP(parts[0])
+	if len(host) == 0 {
+		return nil, nil
+	}
+
+	log.Debugf("Lookup for IP %s: name %s", parts[0], host)
+	fqdn := dns.Fqdn(host)
+
+	resp := new(dns.Msg)
+	resp.SetReply(query)
+
+	rr := new(dns.PTR)
+	rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
+	rr.Ptr = fqdn
+	resp.Answer = append(resp.Answer, rr)
+	return resp, nil
+}
+
+func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
+	var (
+		resp *dns.Msg
+		err  error
+	)
+
+	name := query.Question[0].Name
+	if query.Question[0].Qtype == dns.TypeA {
+		resp, err = r.handleIPv4Query(name, query)
+	} else if query.Question[0].Qtype == dns.TypePTR {
+		resp, err = r.handlePTRQuery(name, query)
+	}
+
+	if err != nil {
+		log.Error(err)
+		return
+	}
+
+	if resp == nil {
+		if len(r.extDNS) == 0 {
+			return
+		}
+		log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[0], name, query.Question[0].Qtype)
+
+		c := &dns.Client{Net: "udp"}
+		addr := fmt.Sprintf("%s:%d", r.extDNS[0], 53)
+
+		// TODO: iterate over avilable servers in case of error
+		resp, _, err = c.Exchange(query, addr)
+		if err != nil {
+			log.Errorf("external resolution failed, %s", err)
+			return
+		}
+	}
+
+	err = w.WriteMsg(resp)
+	if err != nil {
+		log.Errorf("error writing resolver resp, %s", err)
+	}
+}

+ 174 - 6
vendor/src/github.com/docker/libnetwork/sandbox.go

@@ -5,9 +5,11 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"net"
 	"os"
 	"path"
 	"path/filepath"
+	"strings"
 	"sync"
 
 	log "github.com/Sirupsen/logrus"
@@ -38,6 +40,12 @@ type Sandbox interface {
 	Rename(name string) error
 	// Delete destroys this container after detaching it from all connected endpoints.
 	Delete() error
+	// ResolveName searches for the service name in the networks to which the sandbox
+	// is connected to.
+	ResolveName(name string) net.IP
+	// ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
+	// notation; the format used for DNS PTR records
+	ResolveIP(name string) string
 }
 
 // SandboxOption is a option setter function type used to pass varios options to
@@ -59,8 +67,11 @@ type sandbox struct {
 	id            string
 	containerID   string
 	config        containerConfig
+	extDNS        []string
 	osSbox        osl.Sandbox
 	controller    *controller
+	resolver      Resolver
+	resolverOnce  sync.Once
 	refCnt        int
 	endpoints     epHeap
 	epPriority    map[string]int
@@ -202,6 +213,10 @@ func (sb *sandbox) Delete() error {
 	// likely not required any more. Drop it.
 	etchosts.Drop(sb.config.hostsPath)
 
+	if sb.resolver != nil {
+		sb.resolver.Stop()
+	}
+
 	if sb.osSbox != nil && !sb.config.useDefaultSandBox {
 		sb.osSbox.Destroy()
 	}
@@ -291,6 +306,26 @@ func (sb *sandbox) UnmarshalJSON(b []byte) (err error) {
 	return nil
 }
 
+func (sb *sandbox) startResolver() {
+	sb.resolverOnce.Do(func() {
+		var err error
+		sb.resolver = NewResolver(sb)
+		defer func() {
+			if err != nil {
+				sb.resolver = nil
+			}
+		}()
+
+		sb.rebuildDNS()
+		sb.resolver.SetExtServers(sb.extDNS)
+
+		sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
+		if err := sb.resolver.Start(); err != nil {
+			log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
+		}
+	})
+}
+
 func (sb *sandbox) setupResolutionFiles() error {
 	if err := sb.buildHostsFile(); err != nil {
 		return err
@@ -361,6 +396,97 @@ func (sb *sandbox) updateGateway(ep *endpoint) error {
 	return nil
 }
 
+func (sb *sandbox) ResolveIP(ip string) string {
+	var svc string
+	log.Debugf("IP To resolve %v", ip)
+
+	for _, ep := range sb.getConnectedEndpoints() {
+		n := ep.getNetwork()
+
+		sr, ok := n.getController().svcDb[n.ID()]
+		if !ok {
+			continue
+		}
+
+		nwName := n.Name()
+		n.Lock()
+		svc, ok = sr.ipMap[ip]
+		n.Unlock()
+		if ok {
+			return svc + "." + nwName
+		}
+	}
+	return svc
+}
+
+func (sb *sandbox) ResolveName(name string) net.IP {
+	var ip net.IP
+	parts := strings.Split(name, ".")
+	log.Debugf("To resolve %v", parts)
+
+	reqName := parts[0]
+	networkName := ""
+	if len(parts) > 1 {
+		networkName = parts[1]
+	}
+	epList := sb.getConnectedEndpoints()
+	// First check for local container alias
+	ip = sb.resolveName(reqName, networkName, epList, true)
+	if ip != nil {
+		return ip
+	}
+
+	// Resolve the actual container name
+	return sb.resolveName(reqName, networkName, epList, false)
+}
+
+func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) net.IP {
+	for _, ep := range epList {
+		name := req
+		n := ep.getNetwork()
+
+		if networkName != "" && networkName != n.Name() {
+			continue
+		}
+
+		if alias {
+			if ep.aliases == nil {
+				continue
+			}
+
+			var ok bool
+			ep.Lock()
+			name, ok = ep.aliases[req]
+			ep.Unlock()
+			if !ok {
+				continue
+			}
+		} else {
+			// If it is a regular lookup and if the requested name is an alias
+			// dont perform a svc lookup for this endpoint.
+			ep.Lock()
+			if _, ok := ep.aliases[req]; ok {
+				ep.Unlock()
+				continue
+			}
+			ep.Unlock()
+		}
+
+		sr, ok := n.getController().svcDb[n.ID()]
+		if !ok {
+			continue
+		}
+
+		n.Lock()
+		ip, ok := sr.svcMap[name]
+		n.Unlock()
+		if ok {
+			return ip[0]
+		}
+	}
+	return nil
+}
+
 func (sb *sandbox) SetKey(basePath string) error {
 	var err error
 	if basePath == "" {
@@ -459,6 +585,10 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
 	i := ep.iface
 	ep.Unlock()
 
+	if ep.needResolver() {
+		sb.startResolver()
+	}
+
 	if i != nil && i.srcName != "" {
 		var ifaceOptions []osl.IfaceOption
 
@@ -599,7 +729,7 @@ func (sb *sandbox) buildHostsFile() error {
 	return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent)
 }
 
-func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record) error {
+func (sb *sandbox) updateHostsFile(ifaceIP string) error {
 	var mhost string
 
 	if sb.config.originHostsPath != "" {
@@ -613,11 +743,7 @@ func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record)
 		mhost = sb.config.hostName
 	}
 
-	extraContent := make([]etchosts.Record, 0, len(svcRecords)+1)
-	extraContent = append(extraContent, etchosts.Record{Hosts: mhost, IP: ifaceIP})
-	for _, svc := range svcRecords {
-		extraContent = append(extraContent, svc)
-	}
+	extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
 
 	sb.addHostsEntries(extraContent)
 	return nil
@@ -787,6 +913,48 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
 	return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath)
 }
 
+// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
+// resolv.conf by doing the follwing
+// - Save the external name servers in resolv.conf in the sandbox
+// - Add only the embedded server's IP to container's resolv.conf
+// - If the embedded server needs any resolv.conf options add it to the current list
+func (sb *sandbox) rebuildDNS() error {
+	currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
+	if err != nil {
+		return err
+	}
+
+	// localhost entries have already been filtered out from the list
+	sb.extDNS = resolvconf.GetNameservers(currRC.Content)
+
+	var (
+		dnsList        = []string{sb.resolver.NameServer()}
+		dnsOptionsList = resolvconf.GetOptions(currRC.Content)
+		dnsSearchList  = resolvconf.GetSearchDomains(currRC.Content)
+	)
+
+	// Resolver returns the options in the format resolv.conf expects
+	dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)
+
+	dir := path.Dir(sb.config.resolvConfPath)
+	tmpResolvFile, err := ioutil.TempFile(dir, "resolv")
+	if err != nil {
+		return err
+	}
+
+	// Change the perms to filePerm (0644) since ioutil.TempFile creates it by default as 0600
+	if err := os.Chmod(tmpResolvFile.Name(), filePerm); err != nil {
+		return err
+	}
+
+	_, err = resolvconf.Build(tmpResolvFile.Name(), dnsList, dnsSearchList, dnsOptionsList)
+	if err != nil {
+		return err
+	}
+
+	return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath)
+}
+
 // joinLeaveStart waits to ensure there are no joins or leaves in progress and
 // marks this join/leave in progress without race
 func (sb *sandbox) joinLeaveStart() {

+ 4 - 0
vendor/src/github.com/miekg/dns/.gitignore

@@ -0,0 +1,4 @@
+*.6
+tags
+test.out
+a.out

+ 7 - 0
vendor/src/github.com/miekg/dns/.travis.yml

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

+ 1 - 0
vendor/src/github.com/miekg/dns/AUTHORS

@@ -0,0 +1 @@
+Miek Gieben <miek@miek.nl>

+ 9 - 0
vendor/src/github.com/miekg/dns/CONTRIBUTORS

@@ -0,0 +1,9 @@
+Alex A. Skinner
+Andrew Tunnell-Jones
+Ask Bjørn Hansen
+Dave Cheney
+Dusty Wilson
+Marek Majkowski
+Peter van Dijk
+Omri Bahumi
+Alex Sergeyev

+ 9 - 0
vendor/src/github.com/miekg/dns/COPYRIGHT

@@ -0,0 +1,9 @@
+Copyright 2009 The Go Authors. All rights reserved. Use of this source code
+is governed by a BSD-style license that can be found in the LICENSE file.
+Extensions of the original work are copyright (c) 2011 Miek Gieben
+
+Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is
+governed by a BSD-style license that can be found in the LICENSE file.
+
+Copyright 2014 CloudFlare. All rights reserved. Use of this source code is
+governed by a BSD-style license that can be found in the LICENSE file.

+ 32 - 0
vendor/src/github.com/miekg/dns/LICENSE

@@ -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.
+

+ 153 - 0
vendor/src/github.com/miekg/dns/README.md

@@ -0,0 +1,153 @@
+[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns)
+
+# Alternative (more granular) approach to a DNS library
+
+> Less is more.
+
+Complete and usable DNS library. All widely used Resource Records are
+supported, including the DNSSEC types. It follows a lean and mean philosophy.
+If there is stuff you should know as a DNS programmer there isn't a convenience
+function for it. Server side and client side programming is supported, i.e. you
+can build servers and resolvers with it.
+
+We try to keep the "master" branch as sane as possible and at the bleeding edge
+of standards, avoiding breaking changes wherever reasonable. We support the last
+two versions of Go, currently: 1.4 and 1.5.
+
+# Goals
+
+* KISS;
+* Fast;
+* Small API, if its easy to code in Go, don't make a function for it.
+
+# Users
+
+A not-so-up-to-date-list-that-may-be-actually-current:
+
+* https://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/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://play.google.com/store/apps/details?id=com.turbobytes.dig
+* https://github.com/fcambus/statzone
+* https://github.com/benschw/dns-clb-go
+* https://github.com/corny/dnscheck for http://public-dns.tk/
+* https://namesmith.io
+* https://github.com/miekg/unbound
+* https://github.com/miekg/exdns
+
+Send pull request if you want to be listed here.
+
+# Features
+
+* UDP/TCP queries, IPv4 and IPv6;
+* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported;
+* Fast:
+    * Reply speed around ~ 80K qps (faster hardware results in more qps);
+    * Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
+* Server side programming (mimicking the net/http package);
+* Client side programming;
+* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
+* EDNS0, NSID;
+* AXFR/IXFR;
+* TSIG, SIG(0);
+* DNS name compression;
+* Depends only on the standard library.
+
+Have fun!
+
+Miek Gieben  -  2010-2012  -  <miek@miek.nl>
+
+# Building
+
+Building is done with the `go` tool. If you have setup your GOPATH
+correctly, the following should work:
+
+    go get github.com/miekg/dns
+    go build github.com/miekg/dns
+
+## Examples
+
+A short "how to use the API" is at the beginning of 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
+* 4025 - IPSECKEY
+* 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
+* 7553 - URI record
+* xxxx - EDNS0 DNS Update Lease (draft)
+
+## Loosely based upon
+
+* `ldns`
+* `NSD`
+* `Net::DNS`
+* `GRONG`
+
+## TODO
+
+* privatekey.Precompute() when signing?
+* Last remaining RRs: APL, ATMA, A6, NSAP and NXT.
+* Missing in parsing: ISDN, UNSPEC, NSAP and ATMA.
+* NSEC(3) cover/match/closest enclose.
+* Replies with TC bit are not parsed to the end.

+ 385 - 0
vendor/src/github.com/miekg/dns/client.go

@@ -0,0 +1,385 @@
+package dns
+
+// A client implementation.
+
+import (
+	"bytes"
+	"io"
+	"net"
+	"time"
+)
+
+const dnsTimeout time.Duration = 2 * time.Second
+const tcpIdleTimeout time.Duration = 8 * time.Second
+
+// A Conn represents a connection to a DNS server.
+type Conn struct {
+	net.Conn                         // a net.Conn holding the connection
+	UDPSize        uint16            // minimum receive buffer for UDP messages
+	TsigSecret     map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
+	rtt            time.Duration
+	t              time.Time
+	tsigRequestMAC string
+}
+
+// A Client defines parameters for a DNS client.
+type Client struct {
+	Net            string            // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
+	UDPSize        uint16            // minimum receive buffer for UDP messages
+	DialTimeout    time.Duration     // net.DialTimeout, 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 fully qualified
+	SingleInflight bool              // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
+	group          singleflight
+}
+
+// Exchange performs a synchronous UDP query. It sends the message m to the address
+// contained in a and waits for an reply. Exchange does not retry a failed query, nor
+// will it fall back to TCP in case of truncation.
+// If you need to send a DNS message on an already existing connection, you can use the
+// following:
+//
+//	co := &dns.Conn{Conn: c} // c is your net.Conn
+//	co.WriteMsg(m)
+//	in, err := co.ReadMsg()
+//	co.Close()
+//
+func Exchange(m *Msg, a string) (r *Msg, err error) {
+	var co *Conn
+	co, err = DialTimeout("udp", a, dnsTimeout)
+	if err != nil {
+		return nil, err
+	}
+
+	defer co.Close()
+
+	opt := m.IsEdns0()
+	// If EDNS0 is used use that for size.
+	if opt != nil && opt.UDPSize() >= MinMsgSize {
+		co.UDPSize = opt.UDPSize()
+	}
+
+	co.SetWriteDeadline(time.Now().Add(dnsTimeout))
+	if err = co.WriteMsg(m); err != nil {
+		return nil, err
+	}
+
+	co.SetReadDeadline(time.Now().Add(dnsTimeout))
+	r, err = co.ReadMsg()
+	if err == nil && r.Id != m.Id {
+		err = ErrId
+	}
+	return r, err
+}
+
+// ExchangeConn performs a synchronous query. It sends the message m via the connection
+// c and waits for a reply. The connection c is not closed by ExchangeConn.
+// This function is going away, but can easily be mimicked:
+//
+//	co := &dns.Conn{Conn: c} // c is your net.Conn
+//	co.WriteMsg(m)
+//	in, _  := co.ReadMsg()
+//	co.Close()
+//
+func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) {
+	println("dns: this function is deprecated")
+	co := new(Conn)
+	co.Conn = c
+	if err = co.WriteMsg(m); err != nil {
+		return nil, err
+	}
+	r, err = co.ReadMsg()
+	if err == nil && r.Id != m.Id {
+		err = ErrId
+	}
+	return r, err
+}
+
+// Exchange performs an synchronous query. It sends the message m to the address
+// contained in a and waits for an reply. Basic use pattern with a *dns.Client:
+//
+//	c := new(dns.Client)
+//	in, rtt, err := c.Exchange(message, "127.0.0.1:53")
+//
+// Exchange does not retry a failed query, nor will it fall back to TCP in
+// case of truncation.
+func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+	if !c.SingleInflight {
+		return c.exchange(m, a)
+	}
+	// This adds a bunch of garbage, TODO(miek).
+	t := "nop"
+	if t1, ok := TypeToString[m.Question[0].Qtype]; ok {
+		t = t1
+	}
+	cl := "nop"
+	if cl1, ok := ClassToString[m.Question[0].Qclass]; ok {
+		cl = cl1
+	}
+	r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) {
+		return c.exchange(m, a)
+	})
+	if err != nil {
+		return r, rtt, err
+	}
+	if shared {
+		return r.Copy(), rtt, nil
+	}
+	return r, rtt, nil
+}
+
+func (c *Client) dialTimeout() time.Duration {
+	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
+}
+
+func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+	var co *Conn
+	if c.Net == "" {
+		co, err = DialTimeout("udp", a, c.dialTimeout())
+	} else {
+		co, err = DialTimeout(c.Net, a, c.dialTimeout())
+	}
+	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
+	co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
+	if err = co.WriteMsg(m); err != nil {
+		return nil, 0, err
+	}
+
+	co.SetReadDeadline(time.Now().Add(c.readTimeout()))
+	r, err = co.ReadMsg()
+	if err == nil && r.Id != m.Id {
+		err = ErrId
+	}
+	return r, co.rtt, err
+}
+
+// ReadMsg reads a message from the connection co.
+// If the received message contains a TSIG record the transaction
+// signature is verified.
+func (co *Conn) ReadMsg() (*Msg, error) {
+	p, err := co.ReadMsgHeader(nil)
+	if err != nil {
+		return nil, err
+	}
+
+	m := new(Msg)
+	if err := m.Unpack(p); err != nil {
+		// If ErrTruncated was returned, we still want to allow the user to use
+		// the message, but naively they can just check err if they don't want
+		// to use a truncated message
+		if err == ErrTruncated {
+			return m, err
+		}
+		return nil, err
+	}
+	if t := m.IsTsig(); t != nil {
+		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
+	)
+
+	if t, ok := co.Conn.(*net.TCPConn); ok {
+		// First two bytes specify the length of the entire message.
+		l, err := tcpMsgLen(t)
+		if err != nil {
+			return nil, err
+		}
+		p = make([]byte, l)
+		n, err = tcpRead(t, p)
+	} else {
+		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 {
+		if _, err = UnpackStruct(hdr, p, 0); err != nil {
+			return nil, err
+		}
+	}
+	return p, err
+}
+
+// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
+func tcpMsgLen(t *net.TCPConn) (int, error) {
+	p := []byte{0, 0}
+	n, err := t.Read(p)
+	if err != nil {
+		return 0, err
+	}
+	if n != 2 {
+		return 0, ErrShortRead
+	}
+	l, _ := unpackUint16(p, 0)
+	if l == 0 {
+		return 0, ErrShortRead
+	}
+	return int(l), nil
+}
+
+// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
+func tcpRead(t *net.TCPConn, 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
+	}
+	if t, ok := co.Conn.(*net.TCPConn); ok {
+		l, err := tcpMsgLen(t)
+		if err != nil {
+			return 0, err
+		}
+		if l > len(p) {
+			return int(l), io.ErrShortBuffer
+		}
+		return tcpRead(t, p[:l])
+	}
+	// UDP connection
+	n, err = co.Conn.Read(p)
+	if err != nil {
+		return n, err
+	}
+
+	co.rtt = time.Since(co.t)
+	return n, err
+}
+
+// WriteMsg sends a message throught the connection co.
+// If the message m contains a TSIG record the transaction
+// signature is calculated.
+func (co *Conn) WriteMsg(m *Msg) (err error) {
+	var out []byte
+	if t := m.IsTsig(); t != nil {
+		mac := ""
+		if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
+			return ErrSecret
+		}
+		out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
+		// Set for the next read, allthough only used in zone transfers
+		co.tsigRequestMAC = mac
+	} else {
+		out, err = m.Pack()
+	}
+	if err != nil {
+		return err
+	}
+	co.t = time.Now()
+	if _, err = co.Write(out); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Write implements the net.Conn Write method.
+func (co *Conn) Write(p []byte) (n int, err error) {
+	if t, ok := co.Conn.(*net.TCPConn); ok {
+		lp := len(p)
+		if lp < 2 {
+			return 0, io.ErrShortBuffer
+		}
+		if lp > MaxMsgSize {
+			return 0, &Error{err: "message too large"}
+		}
+		l := make([]byte, 2, lp+2)
+		l[0], l[1] = packUint16(uint16(lp))
+		p = append(l, p...)
+		n, err := io.Copy(t, bytes.NewReader(p))
+		return int(n), err
+	}
+	n, err = co.Conn.(*net.UDPConn).Write(p)
+	return n, err
+}
+
+// Dial connects to the address on the named network.
+func Dial(network, address string) (conn *Conn, err error) {
+	conn = new(Conn)
+	conn.Conn, err = net.Dial(network, address)
+	if err != nil {
+		return nil, err
+	}
+	return conn, nil
+}
+
+// DialTimeout acts like Dial but takes a timeout.
+func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) {
+	conn = new(Conn)
+	conn.Conn, err = net.DialTimeout(network, address, timeout)
+	if err != nil {
+		return nil, err
+	}
+	return conn, nil
+}

+ 99 - 0
vendor/src/github.com/miekg/dns/clientconfig.go

@@ -0,0 +1,99 @@
+package dns
+
+import (
+	"bufio"
+	"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()
+	c := new(ClientConfig)
+	scanner := bufio.NewScanner(file)
+	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 < 1 {
+						n = 1
+					}
+					c.Ndots = n
+				case len(s) >= 8 && s[:8] == "timeout:":
+					n, _ := strconv.Atoi(s[8:])
+					if n < 1 {
+						n = 1
+					}
+					c.Timeout = n
+				case len(s) >= 8 && s[:9] == "attempts:":
+					n, _ := strconv.Atoi(s[9:])
+					if n < 1 {
+						n = 1
+					}
+					c.Attempts = n
+				case s == "rotate":
+					/* not imp */
+				}
+			}
+		}
+	}
+	return c, nil
+}

+ 278 - 0
vendor/src/github.com/miekg/dns/defaults.go

@@ -0,0 +1,278 @@
+package dns
+
+import (
+	"errors"
+	"net"
+	"strconv"
+)
+
+const hexDigit = "0123456789abcdef"
+
+// Everything is assumed in ClassINET.
+
+// SetReply creates a reply message from a request message.
+func (dns *Msg) SetReply(request *Msg) *Msg {
+	dns.Id = request.Id
+	dns.RecursionDesired = request.RecursionDesired // Copy rd bit
+	dns.Response = true
+	dns.Opcode = OpcodeQuery
+	dns.Rcode = RcodeSuccess
+	if len(request.Question) > 0 {
+		dns.Question = make([]Question, 1)
+		dns.Question[0] = request.Question[0]
+	}
+	return dns
+}
+
+// SetQuestion creates a question message, 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, timesigned int64) *Msg {
+	t := new(TSIG)
+	t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
+	t.Algorithm = algo
+	t.Fudge = 300
+	t.TimeSigned = uint64(timesigned)
+	t.OrigId = dns.Id
+	dns.Extra = append(dns.Extra, t)
+	return dns
+}
+
+// SetEdns0 appends a EDNS0 OPT RR to the message.
+// TSIG should always the last RR in a message.
+func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
+	e := new(OPT)
+	e.Hdr.Name = "."
+	e.Hdr.Rrtype = TypeOPT
+	e.SetUDPSize(udpsize)
+	if do {
+		e.SetDo()
+	}
+	dns.Extra = append(dns.Extra, e)
+	return dns
+}
+
+// IsTsig checks if the message has a TSIG record as the last record
+// in the additional section. It returns the TSIG record found or nil.
+func (dns *Msg) IsTsig() *TSIG {
+	if len(dns.Extra) > 0 {
+		if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
+			return dns.Extra[len(dns.Extra)-1].(*TSIG)
+		}
+	}
+	return nil
+}
+
+// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
+// record in the additional section will do. It returns the OPT record
+// found or nil.
+func (dns *Msg) IsEdns0() *OPT {
+	for _, r := range dns.Extra {
+		if r.Header().Rrtype == TypeOPT {
+			return r.(*OPT)
+		}
+	}
+	return nil
+}
+
+// IsDomainName checks if s is a valid domain name, it returns the number of
+// labels and true, when a domain name is valid.  Note that non fully qualified
+// domain name is considered valid, in this case the last label is counted in
+// the number of labels.  When false is returned the number of labels is not
+// defined.  Also note that this function is extremely liberal; almost any
+// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
+// label fits in 63 characters, but there is no length check for the entire
+// string s. I.e.  a domain name longer than 255 characters is considered valid.
+func IsDomainName(s string) (labels int, ok bool) {
+	_, labels, err := packDomainName(s, nil, 0, nil, false)
+	return labels, err == nil
+}
+
+// IsSubDomain checks if child is indeed a child of the parent. Both child and
+// parent are *not* downcased before doing the comparison.
+func IsSubDomain(parent, child string) bool {
+	// Entire child is contained in parent
+	return CompareDomainName(parent, child) == CountLabel(parent)
+}
+
+// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
+// The checking is performed on the binary payload.
+func IsMsg(buf []byte) error {
+	// Header
+	if len(buf) < 12 {
+		return errors.New("dns: bad message header")
+	}
+	// Header: Opcode
+	// TODO(miek): more checks here, e.g. check all header bits.
+	return nil
+}
+
+// IsFqdn checks if a domain name is fully qualified.
+func IsFqdn(s string) bool {
+	l := len(s)
+	if l == 0 {
+		return false
+	}
+	return s[l-1] == '.'
+}
+
+// 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 ip.To4() != nil {
+		return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." +
+			strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil
+	}
+	// Must be IPv6
+	buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
+	// Add it, in reverse, to the buffer
+	for i := len(ip) - 1; i >= 0; i-- {
+		v := ip[i]
+		buf = append(buf, hexDigit[v&0xF])
+		buf = append(buf, '.')
+		buf = append(buf, hexDigit[v>>4])
+		buf = append(buf, '.')
+	}
+	// Append "ip6.arpa." and return (buf already has the final .)
+	buf = append(buf, "ip6.arpa."...)
+	return string(buf), nil
+}
+
+// String returns the string representation for the type t.
+func (t Type) String() string {
+	if t1, ok := TypeToString[uint16(t)]; ok {
+		return t1
+	}
+	return "TYPE" + strconv.Itoa(int(t))
+}
+
+// String returns the string representation for the class c.
+func (c Class) String() string {
+	if c1, ok := ClassToString[uint16(c)]; ok {
+		return c1
+	}
+	return "CLASS" + strconv.Itoa(int(c))
+}
+
+// String returns the string representation for the name n.
+func (n Name) String() string {
+	return sprintName(string(n))
+}

+ 100 - 0
vendor/src/github.com/miekg/dns/dns.go

@@ -0,0 +1,100 @@
+package dns
+
+import "strconv"
+
+const (
+	year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
+	// 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
+	defaultTtl = 3600 // Default internal TTL.
+)
+
+// Error represents a DNS error
+type Error struct{ err string }
+
+func (e *Error) Error() string {
+	if e == nil {
+		return "dns: <nil>"
+	}
+	return "dns: " + e.err
+}
+
+// An RR represents a resource record.
+type RR interface {
+	// Header returns the header of an resource record. The header contains
+	// everything up to the rdata.
+	Header() *RR_Header
+	// String returns the text representation of the resource record.
+	String() string
+	// copy returns a copy of the RR
+	copy() RR
+	// len returns the length (in octets) of the uncompressed RR in wire format.
+	len() int
+}
+
+// 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 implement the RR interface.
+func (h *RR_Header) Header() *RR_Header { return h }
+
+// Just to imlement the RR interface.
+func (h *RR_Header) copy() RR { return nil }
+
+func (h *RR_Header) copyHeader() *RR_Header {
+	r := new(RR_Header)
+	r.Name = h.Name
+	r.Rrtype = h.Rrtype
+	r.Class = h.Class
+	r.Ttl = h.Ttl
+	r.Rdlength = h.Rdlength
+	return r
+}
+
+func (h *RR_Header) String() string {
+	var s string
+
+	if h.Rrtype == TypeOPT {
+		s = ";"
+		// and maybe other things
+	}
+
+	s += sprintName(h.Name) + "\t"
+	s += strconv.FormatInt(int64(h.Ttl), 10) + "\t"
+	s += Class(h.Class).String() + "\t"
+	s += Type(h.Rrtype).String() + "\t"
+	return s
+}
+
+func (h *RR_Header) len() int {
+	l := len(h.Name) + 1
+	l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
+	return l
+}
+
+// ToRFC3597 converts a known RR to the unknown RR representation
+// from RFC 3597.
+func (rr *RFC3597) ToRFC3597(r RR) error {
+	buf := make([]byte, r.len()*2)
+	off, err := PackStruct(r, buf, 0)
+	if err != nil {
+		return err
+	}
+	buf = buf[:off]
+	rawSetRdlength(buf, 0, off)
+	_, err = UnpackStruct(rr, buf, 0)
+	if err != nil {
+		return err
+	}
+	return nil
+}

+ 664 - 0
vendor/src/github.com/miekg/dns/dnssec.go

@@ -0,0 +1,664 @@
+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/hex"
+	"math/big"
+	"sort"
+	"strings"
+	"time"
+)
+
+// DNSSEC encryption algorithm codes.
+const (
+	_ uint8 = iota
+	RSAMD5
+	DH
+	DSA
+	_ // Skip 4, RFC 6725, section 2.1
+	RSASHA1
+	DSANSEC3SHA1
+	RSASHA1NSEC3SHA1
+	RSASHA256
+	_ // Skip 9, RFC 6725, section 2.1
+	RSASHA512
+	_ // Skip 11, RFC 6725, section 2.1
+	ECCGOST
+	ECDSAP256SHA256
+	ECDSAP384SHA384
+	INDIRECT   uint8 = 252
+	PRIVATEDNS uint8 = 253 // Private (experimental keys)
+	PRIVATEOID uint8 = 254
+)
+
+// Map for algorithm names.
+var AlgorithmToString = map[uint8]string{
+	RSAMD5:           "RSAMD5",
+	DH:               "DH",
+	DSA:              "DSA",
+	RSASHA1:          "RSASHA1",
+	DSANSEC3SHA1:     "DSA-NSEC3-SHA1",
+	RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
+	RSASHA256:        "RSASHA256",
+	RSASHA512:        "RSASHA512",
+	ECCGOST:          "ECC-GOST",
+	ECDSAP256SHA256:  "ECDSAP256SHA256",
+	ECDSAP384SHA384:  "ECDSAP384SHA384",
+	INDIRECT:         "INDIRECT",
+	PRIVATEDNS:       "PRIVATEDNS",
+	PRIVATEOID:       "PRIVATEOID",
+}
+
+// Map of algorithm strings.
+var StringToAlgorithm = reverseInt8(AlgorithmToString)
+
+// Map of algorithm crypto hashes.
+var AlgorithmToHash = map[uint8]crypto.Hash{
+	RSAMD5:           crypto.MD5, // Deprecated in RFC 6725
+	RSASHA1:          crypto.SHA1,
+	RSASHA1NSEC3SHA1: crypto.SHA1,
+	RSASHA256:        crypto.SHA256,
+	ECDSAP256SHA256:  crypto.SHA256,
+	ECDSAP384SHA384:  crypto.SHA384,
+	RSASHA512:        crypto.SHA512,
+}
+
+// DNSSEC hashing algorithm codes.
+const (
+	_      uint8 = iota
+	SHA1         // RFC 4034
+	SHA256       // RFC 4509
+	GOST94       // RFC 5933
+	SHA384       // Experimental
+	SHA512       // Experimental
+)
+
+// Map for hash names.
+var HashToString = map[uint8]string{
+	SHA1:   "SHA1",
+	SHA256: "SHA256",
+	GOST94: "GOST94",
+	SHA384: "SHA384",
+	SHA512: "SHA512",
+}
+
+// Map of hash strings.
+var StringToHash = reverseInt8(HashToString)
+
+// DNSKEY flag values.
+const (
+	SEP    = 1
+	REVOKE = 1 << 7
+	ZONE   = 1 << 8
+)
+
+// The RRSIG needs to be converted to wireformat with some of
+// the rdata (the signature) missing. Use this struct to ease
+// the conversion (and re-use the pack/unpack functions).
+type rrsigWireFmt struct {
+	TypeCovered uint16
+	Algorithm   uint8
+	Labels      uint8
+	OrigTtl     uint32
+	Expiration  uint32
+	Inception   uint32
+	KeyTag      uint16
+	SignerName  string `dns:"domain-name"`
+	/* No Signature */
+}
+
+// Used for converting DNSKEY's rdata to wirefmt.
+type dnskeyWireFmt struct {
+	Flags     uint16
+	Protocol  uint8
+	Algorithm uint8
+	PublicKey string `dns:"base64"`
+	/* Nothing is left out */
+}
+
+func divRoundUp(a, b int) int {
+	return (a + b - 1) / b
+}
+
+// KeyTag calculates the keytag (or key-id) of the DNSKEY.
+func (k *DNSKEY) KeyTag() uint16 {
+	if k == nil {
+		return 0
+	}
+	var keytag int
+	switch k.Algorithm {
+	case RSAMD5:
+		// Look at the bottom two bytes of the modules, which the last
+		// item in the pubkey. We could do this faster by looking directly
+		// at the base64 values. But I'm lazy.
+		modulus, _ := fromBase64([]byte(k.PublicKey))
+		if len(modulus) > 1 {
+			x, _ := unpackUint16(modulus, len(modulus)-2)
+			keytag = int(x)
+		}
+	default:
+		keywire := new(dnskeyWireFmt)
+		keywire.Flags = k.Flags
+		keywire.Protocol = k.Protocol
+		keywire.Algorithm = k.Algorithm
+		keywire.PublicKey = k.PublicKey
+		wire := make([]byte, DefaultMsgSize)
+		n, err := PackStruct(keywire, wire, 0)
+		if err != nil {
+			return 0
+		}
+		wire = wire[:n]
+		for i, v := range wire {
+			if i&1 != 0 {
+				keytag += int(v) // must be larger than uint32
+			} else {
+				keytag += int(v) << 8
+			}
+		}
+		keytag += (keytag >> 16) & 0xFFFF
+		keytag &= 0xFFFF
+	}
+	return uint16(keytag)
+}
+
+// ToDS converts a DNSKEY record to a DS record.
+func (k *DNSKEY) ToDS(h uint8) *DS {
+	if k == nil {
+		return nil
+	}
+	ds := new(DS)
+	ds.Hdr.Name = k.Hdr.Name
+	ds.Hdr.Class = k.Hdr.Class
+	ds.Hdr.Rrtype = TypeDS
+	ds.Hdr.Ttl = k.Hdr.Ttl
+	ds.Algorithm = k.Algorithm
+	ds.DigestType = h
+	ds.KeyTag = k.KeyTag()
+
+	keywire := new(dnskeyWireFmt)
+	keywire.Flags = k.Flags
+	keywire.Protocol = k.Protocol
+	keywire.Algorithm = k.Algorithm
+	keywire.PublicKey = k.PublicKey
+	wire := make([]byte, DefaultMsgSize)
+	n, err := PackStruct(keywire, wire, 0)
+	if err != nil {
+		return nil
+	}
+	wire = wire[:n]
+
+	owner := make([]byte, 255)
+	off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false)
+	if err1 != nil {
+		return nil
+	}
+	owner = owner[:off]
+	// RFC4034:
+	// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
+	// "|" denotes concatenation
+	// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
+
+	// digest buffer
+	digest := append(owner, wire...) // another copy
+
+	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(digest)
+	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.copyHeader()
+	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.copyHeader()
+	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
+	}
+
+	rr.Hdr.Rrtype = TypeRRSIG
+	rr.Hdr.Name = rrset[0].Header().Name
+	rr.Hdr.Class = rrset[0].Header().Class
+	if rr.OrigTtl == 0 { // If set don't override
+		rr.OrigTtl = rrset[0].Header().Ttl
+	}
+	rr.TypeCovered = rrset[0].Header().Rrtype
+	rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
+
+	if strings.HasPrefix(rrset[0].Header().Name, "*") {
+		rr.Labels-- // wildcard, remove from label count
+	}
+
+	sigwire := new(rrsigWireFmt)
+	sigwire.TypeCovered = rr.TypeCovered
+	sigwire.Algorithm = rr.Algorithm
+	sigwire.Labels = rr.Labels
+	sigwire.OrigTtl = rr.OrigTtl
+	sigwire.Expiration = rr.Expiration
+	sigwire.Inception = rr.Inception
+	sigwire.KeyTag = rr.KeyTag
+	// For signing, lowercase this name
+	sigwire.SignerName = strings.ToLower(rr.SignerName)
+
+	// Create the desired binary blob
+	signdata := make([]byte, DefaultMsgSize)
+	n, err := PackStruct(sigwire, signdata, 0)
+	if err != nil {
+		return err
+	}
+	signdata = signdata[:n]
+	wire, err := rawSignatureData(rrset, rr)
+	if err != nil {
+		return err
+	}
+	signdata = append(signdata, wire...)
+
+	hash, ok := AlgorithmToHash[rr.Algorithm]
+	if !ok {
+		return ErrAlg
+	}
+
+	h := hash.New()
+	h.Write(signdata)
+
+	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
+	}
+
+	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.ToLower(rr.SignerName) != strings.ToLower(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 rrset[0].Header().Class != rr.Hdr.Class {
+		return ErrRRset
+	}
+	if rrset[0].Header().Rrtype != rr.TypeCovered {
+		return ErrRRset
+	}
+
+	// RFC 4035 5.3.2.  Reconstructing the Signed Data
+	// Copy the sig, except the rrsig data
+	sigwire := new(rrsigWireFmt)
+	sigwire.TypeCovered = rr.TypeCovered
+	sigwire.Algorithm = rr.Algorithm
+	sigwire.Labels = rr.Labels
+	sigwire.OrigTtl = rr.OrigTtl
+	sigwire.Expiration = rr.Expiration
+	sigwire.Inception = rr.Inception
+	sigwire.KeyTag = rr.KeyTag
+	sigwire.SignerName = strings.ToLower(rr.SignerName)
+	// Create the desired binary blob
+	signeddata := make([]byte, DefaultMsgSize)
+	n, err := PackStruct(sigwire, signeddata, 0)
+	if err != nil {
+		return err
+	}
+	signeddata = signeddata[:n]
+	wire, err := rawSignatureData(rrset, rr)
+	if err != nil {
+		return err
+	}
+	signeddata = append(signeddata, wire...)
+
+	sigbuf := rr.sigBuf()           // Get the binary signature data
+	if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
+		// TODO(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)
+		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)
+		if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
+			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
+	}
+
+	// RFC 2537/3110, section 2. RSA Public KEY Resource Records
+	// Length is in the 0th byte, unless its zero, then it
+	// it in bytes 1 and 2 and its a 16 bit number
+	explen := uint16(keybuf[0])
+	keyoff := 1
+	if explen == 0 {
+		explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
+		keyoff = 3
+	}
+	pubkey := new(rsa.PublicKey)
+
+	pubkey.N = big.NewInt(0)
+	shift := uint64((explen - 1) * 8)
+	expo := uint64(0)
+	for i := int(explen - 1); i > 0; i-- {
+		expo += uint64(keybuf[keyoff+i]) << shift
+		shift -= 8
+	}
+	// Remainder
+	expo += uint64(keybuf[keyoff])
+	if expo > 2<<31 {
+		// Larger expo than supported.
+		// println("dns: F5 primes (or larger) are not supported")
+		return nil
+	}
+	pubkey.E = int(expo)
+
+	pubkey.N.SetBytes(keybuf[keyoff+int(explen):])
+	return pubkey
+}
+
+// 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
+}
+
+type wireSlice [][]byte
+
+func (p wireSlice) Len() int      { return len(p) }
+func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p wireSlice) Less(i, j int) bool {
+	_, ioff, _ := UnpackDomainName(p[i], 0)
+	_, joff, _ := UnpackDomainName(p[j], 0)
+	return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0
+}
+
+// Return the raw signature data.
+func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
+	wires := make(wireSlice, len(rrset))
+	for i, r := range rrset {
+		r1 := r.copy()
+		r1.Header().Ttl = s.OrigTtl
+		labels := SplitDomainName(r1.Header().Name)
+		// 6.2. Canonical RR Form. (4) - wildcards
+		if len(labels) > int(s.Labels) {
+			// Wildcard
+			r1.Header().Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
+		}
+		// RFC 4034: 6.2.  Canonical RR Form. (2) - domain name to lowercase
+		r1.Header().Name = strings.ToLower(r1.Header().Name)
+		// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
+		//   NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
+		//   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
+		//   SRV, DNAME, A6
+		//
+		// 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 *CNAME:
+			x.Target = strings.ToLower(x.Target)
+		case *SOA:
+			x.Ns = strings.ToLower(x.Ns)
+			x.Mbox = strings.ToLower(x.Mbox)
+		case *MB:
+			x.Mb = strings.ToLower(x.Mb)
+		case *MG:
+			x.Mg = strings.ToLower(x.Mg)
+		case *MR:
+			x.Mr = strings.ToLower(x.Mr)
+		case *PTR:
+			x.Ptr = strings.ToLower(x.Ptr)
+		case *MINFO:
+			x.Rmail = strings.ToLower(x.Rmail)
+			x.Email = strings.ToLower(x.Email)
+		case *MX:
+			x.Mx = strings.ToLower(x.Mx)
+		case *NAPTR:
+			x.Replacement = strings.ToLower(x.Replacement)
+		case *KX:
+			x.Exchanger = strings.ToLower(x.Exchanger)
+		case *SRV:
+			x.Target = strings.ToLower(x.Target)
+		case *DNAME:
+			x.Target = strings.ToLower(x.Target)
+		}
+		// 6.2. Canonical RR Form. (5) - origTTL
+		wire := make([]byte, r1.len()+1) // +1 to be safe(r)
+		off, err1 := PackRR(r1, wire, 0, nil, false)
+		if err1 != nil {
+			return nil, err1
+		}
+		wire = wire[:off]
+		wires[i] = wire
+	}
+	sort.Sort(wires)
+	for i, wire := range wires {
+		if i > 0 && bytes.Equal(wire, wires[i-1]) {
+			continue
+		}
+		buf = append(buf, wire...)
+	}
+	return buf, nil
+}

+ 156 - 0
vendor/src/github.com/miekg/dns/dnssec_keygen.go

@@ -0,0 +1,156 @@
+package dns
+
+import (
+	"crypto"
+	"crypto/dsa"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/rsa"
+	"math/big"
+)
+
+// 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
+		}
+	}
+
+	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
+	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 (the values E and N) for RSA
+// RFC 3110: Section 2. RSA Public KEY Resource Records
+func exponentToBuf(_E int) []byte {
+	var buf []byte
+	i := big.NewInt(int64(_E))
+	if len(i.Bytes()) < 256 {
+		buf = make([]byte, 1)
+		buf[0] = uint8(len(i.Bytes()))
+	} else {
+		buf = make([]byte, 3)
+		buf[0] = 0
+		buf[1] = uint8(len(i.Bytes()) >> 8)
+		buf[2] = uint8(len(i.Bytes()))
+	}
+	buf = append(buf, i.Bytes()...)
+	return buf
+}
+
+// Set the public key for X and Y for Curve. The two
+// values are just concatenated.
+func curveToBuf(_X, _Y *big.Int, intlen int) []byte {
+	buf := intToBytes(_X, intlen)
+	buf = append(buf, intToBytes(_Y, intlen)...)
+	return buf
+}
+
+// Set the public key for X and Y for Curve. The two
+// values are just concatenated.
+func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte {
+	t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8)
+	buf := []byte{byte(t)}
+	buf = append(buf, intToBytes(_Q, 20)...)
+	buf = append(buf, intToBytes(_P, 64+t*8)...)
+	buf = append(buf, intToBytes(_G, 64+t*8)...)
+	buf = append(buf, intToBytes(_Y, 64+t*8)...)
+	return buf
+}

+ 249 - 0
vendor/src/github.com/miekg/dns/dnssec_keyscan.go

@@ -0,0 +1,249 @@
+package dns
+
+import (
+	"crypto"
+	"crypto/dsa"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"io"
+	"math/big"
+	"strconv"
+	"strings"
+)
+
+// 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[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, e := parseKey(q, file)
+	if m == nil {
+		return nil, e
+	}
+	if _, ok := m["private-key-format"]; !ok {
+		return nil, ErrPrivKey
+	}
+	if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
+		return nil, ErrPrivKey
+	}
+	// TODO(mg): check if the pubkey matches the private key
+	algo, err := strconv.Atoi(strings.SplitN(m["algorithm"], " ", 2)[0])
+	if err != nil {
+		return nil, ErrPrivKey
+	}
+	switch uint8(algo) {
+	case DSA:
+		priv, e := readPrivateKeyDSA(m)
+		if e != nil {
+			return nil, e
+		}
+		pub := k.publicKeyDSA()
+		if pub == nil {
+			return nil, ErrKey
+		}
+		priv.PublicKey = *pub
+		return priv, e
+	case RSAMD5:
+		fallthrough
+	case RSASHA1:
+		fallthrough
+	case RSASHA1NSEC3SHA1:
+		fallthrough
+	case RSASHA256:
+		fallthrough
+	case RSASHA512:
+		priv, e := readPrivateKeyRSA(m)
+		if e != nil {
+			return nil, e
+		}
+		pub := k.publicKeyRSA()
+		if pub == nil {
+			return nil, ErrKey
+		}
+		priv.PublicKey = *pub
+		return priv, e
+	case ECCGOST:
+		return nil, ErrPrivKey
+	case ECDSAP256SHA256:
+		fallthrough
+	case ECDSAP384SHA384:
+		priv, e := readPrivateKeyECDSA(m)
+		if e != nil {
+			return nil, e
+		}
+		pub := k.publicKeyECDSA()
+		if pub == nil {
+			return nil, ErrKey
+		}
+		priv.PublicKey = *pub
+		return priv, e
+	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
+}
+
+// parseKey reads a private key from r. It returns a map[string]string,
+// with the key-value pairs, or an error when the file is not correct.
+func parseKey(r io.Reader, file string) (map[string]string, error) {
+	s := scanInit(r)
+	m := make(map[string]string)
+	c := make(chan lex)
+	k := ""
+	// Start the lexer
+	go klexer(s, c)
+	for l := range c {
+		// It should alternate
+		switch l.value {
+		case zKey:
+			k = l.token
+		case zValue:
+			if k == "" {
+				return nil, &ParseError{file, "no private key seen", l}
+			}
+			//println("Setting", strings.ToLower(k), "to", l.token, "b")
+			m[strings.ToLower(k)] = l.token
+			k = ""
+		}
+	}
+	return m, nil
+}
+
+// klexer scans the sourcefile and returns tokens on the channel c.
+func klexer(s *scan, c chan lex) {
+	var l lex
+	str := "" // Hold the current read text
+	commt := false
+	key := true
+	x, err := s.tokenText()
+	defer close(c)
+	for err == nil {
+		l.column = s.position.Column
+		l.line = s.position.Line
+		switch x {
+		case ':':
+			if commt {
+				break
+			}
+			l.token = str
+			if key {
+				l.value = zKey
+				c <- l
+				// Next token is a space, eat it
+				s.tokenText()
+				key = false
+				str = ""
+			} else {
+				l.value = zValue
+			}
+		case ';':
+			commt = true
+		case '\n':
+			if commt {
+				// Reset a comment
+				commt = false
+			}
+			l.value = zValue
+			l.token = str
+			c <- l
+			str = ""
+			commt = false
+			key = true
+		default:
+			if commt {
+				break
+			}
+			str += string(x)
+		}
+		x, err = s.tokenText()
+	}
+	if len(str) > 0 {
+		// Send remainder
+		l.token = str
+		l.value = zValue
+		c <- l
+	}
+}

+ 85 - 0
vendor/src/github.com/miekg/dns/dnssec_privkey.go

@@ -0,0 +1,85 @@
+package dns
+
+import (
+	"crypto"
+	"crypto/dsa"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"math/big"
+	"strconv"
+)
+
+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"
+
+	default:
+		return ""
+	}
+}

+ 251 - 0
vendor/src/github.com/miekg/dns/doc.go

@@ -0,0 +1,251 @@
+/*
+Package dns implements a full featured interface to the Domain Name System.
+Server- and client-side programming is supported.
+The package allows complete control over what is send out to the DNS. The package
+API follows the less-is-more principle, by presenting a small, clean interface.
+
+The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers,
+TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
+Note that domain names MUST be fully qualified, before sending them, unqualified
+names in a message will result in a packing failure.
+
+Resource records are native types. They are not stored in wire format.
+Basic usage pattern for creating a new resource record:
+
+     r := new(dns.MX)
+     r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
+	Class: dns.ClassINET, Ttl: 3600}
+     r.Preference = 10
+     r.Mx = "mx.miek.nl."
+
+Or directly from a string:
+
+     mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
+
+Or when the default TTL (3600) and class (IN) suit you:
+
+     mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
+
+Or even:
+
+     mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
+
+In the DNS messages are exchanged, these messages contain resource
+records (sets).  Use pattern for creating a message:
+
+     m := new(dns.Msg)
+     m.SetQuestion("miek.nl.", dns.TypeMX)
+
+Or when not certain if the domain name is fully qualified:
+
+	m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
+
+The message m is now a message with the question section set to ask
+the MX records for the miek.nl. zone.
+
+The following is slightly more verbose, but more flexible:
+
+     m1 := new(dns.Msg)
+     m1.Id = dns.Id()
+     m1.RecursionDesired = true
+     m1.Question = make([]dns.Question, 1)
+     m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
+
+After creating a message it can be send.
+Basic use pattern for synchronous querying the DNS at a
+server configured on 127.0.0.1 and port 53:
+
+     c := new(dns.Client)
+     in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
+
+Suppressing multiple outstanding queries (with the same question, type and
+class) is as easy as setting:
+
+	c.SingleInflight = true
+
+If these "advanced" features are not needed, a simple UDP query can be send,
+with:
+
+	in, err := dns.Exchange(m1, "127.0.0.1:53")
+
+When this functions returns you will get dns message. A dns message consists
+out of four sections.
+The question section: in.Question, the answer section: in.Answer,
+the authority section: in.Ns and the additional section: in.Extra.
+
+Each of these sections (except the Question section) contain a []RR. Basic
+use pattern for accessing the rdata of a TXT RR as the first RR in
+the Answer section:
+
+	if t, ok := in.Answer[0].(*dns.TXT); ok {
+		// do something with t.Txt
+	}
+
+Domain Name and TXT Character String Representations
+
+Both domain names and TXT character strings are converted to presentation
+form both when unpacked and when converted to strings.
+
+For TXT character strings, tabs, carriage returns and line feeds will be
+converted to \t, \r and \n respectively. Back slashes and quotations marks
+will be escaped. Bytes below 32 and above 127 will be converted to \DDD
+form.
+
+For domain names, in addition to the above rules brackets, periods,
+spaces, semicolons and the at symbol are escaped.
+
+DNSSEC
+
+DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It
+uses public key cryptography to sign resource records. The
+public keys are stored in DNSKEY records and the signatures in RRSIG records.
+
+Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit
+to an request.
+
+     m := new(dns.Msg)
+     m.SetEdns0(4096, true)
+
+Signature generation, signature verification and key generation are all supported.
+
+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==":
+
+	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() {
+			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 http://miek.nl/posts/2014/Sep/21/Private%20RRs%20and%20IDN%20in%20Go%20DNS/ 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

+ 505 - 0
vendor/src/github.com/miekg/dns/edns.go

@@ -0,0 +1,505 @@
+package dns
+
+import (
+	"encoding/hex"
+	"errors"
+	"net"
+	"strconv"
+)
+
+// EDNS0 Option codes.
+const (
+	EDNS0LLQ         = 0x1     // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
+	EDNS0UL          = 0x2     // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
+	EDNS0NSID        = 0x3     // nsid (RFC5001)
+	EDNS0DAU         = 0x5     // DNSSEC Algorithm Understood
+	EDNS0DHU         = 0x6     // DS Hash Understood
+	EDNS0N3U         = 0x7     // NSEC3 Hash Understood
+	EDNS0SUBNET      = 0x8     // client-subnet (RFC6891)
+	EDNS0EXPIRE      = 0x9     // EDNS0 expire
+	EDNS0SUBNETDRAFT = 0x50fa  // Don't use! Use EDNS0SUBNET
+	EDNS0LOCALSTART  = 0xFDE9  // Beginning of range reserved for local/experimental use (RFC6891)
+	EDNS0LOCALEND    = 0xFFFE  // End of range reserved for local/experimental use (RFC6891)
+	_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()
+			if o.(*EDNS0_SUBNET).DraftOption {
+				s += " (draft)"
+			}
+		case *EDNS0_UL:
+			s += "\n; UPDATE LEASE: " + o.String()
+		case *EDNS0_LLQ:
+			s += "\n; LONG LIVED QUERIES: " + o.String()
+		case *EDNS0_DAU:
+			s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String()
+		case *EDNS0_DHU:
+			s += "\n; DS HASH UNDERSTOOD: " + o.String()
+		case *EDNS0_N3U:
+			s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
+		case *EDNS0_LOCAL:
+			s += "\n; LOCAL OPT: " + o.String()
+		}
+	}
+	return s
+}
+
+func (rr *OPT) len() int {
+	l := rr.Hdr.len()
+	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
+}
+
+// return the old value -> delete SetVersion?
+
+// Version returns the EDNS version used. Only zero is defined.
+func (rr *OPT) Version() uint8 {
+	return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16)
+}
+
+// SetVersion sets the version of EDNS. This is usually zero.
+func (rr *OPT) SetVersion(v uint8) {
+	rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | (uint32(v) << 16)
+}
+
+// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
+func (rr *OPT) ExtendedRcode() uint8 {
+	return uint8((rr.Hdr.Ttl & 0xFF000000) >> 24)
+}
+
+// SetExtendedRcode sets the EDNS extended RCODE field.
+func (rr *OPT) SetExtendedRcode(v uint8) {
+	rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24)
+}
+
+// UDPSize returns the UDP buffer size.
+func (rr *OPT) UDPSize() uint16 {
+	return rr.Hdr.Class
+}
+
+// SetUDPSize sets the UDP buffer size.
+func (rr *OPT) SetUDPSize(size uint16) {
+	rr.Hdr.Class = size
+}
+
+// Do returns the value of the DO (DNSSEC OK) bit.
+func (rr *OPT) Do() bool {
+	return rr.Hdr.Ttl&_DO == _DO
+}
+
+// SetDo sets the DO (DNSSEC OK) bit.
+func (rr *OPT) SetDo() {
+	rr.Hdr.Ttl |= _DO
+}
+
+// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to
+// it.
+type EDNS0 interface {
+	// Option returns the option code for the option.
+	Option() uint16
+	// pack returns the bytes of the option data.
+	pack() ([]byte, error)
+	// unpack sets the data as found in the buffer. Is also sets
+	// the length of the slice as the length of the option data.
+	unpack([]byte) error
+	// String returns the string representation of the option.
+	String() string
+}
+
+// The nsid EDNS0 option is used to retrieve a nameserver
+// identifier. When sending a request Nsid must be set to the empty string
+// The identifier is an opaque string encoded as hex.
+// Basic use pattern for creating an nsid option:
+//
+//	o := new(dns.OPT)
+//	o.Hdr.Name = "."
+//	o.Hdr.Rrtype = dns.TypeOPT
+//	e := new(dns.EDNS0_NSID)
+//	e.Code = dns.EDNS0NSID
+//	e.Nsid = "AA"
+//	o.Option = append(o.Option, e)
+type EDNS0_NSID struct {
+	Code uint16 // Always EDNS0NSID
+	Nsid string // This string needs to be hex encoded
+}
+
+func (e *EDNS0_NSID) pack() ([]byte, error) {
+	h, err := hex.DecodeString(e.Nsid)
+	if err != nil {
+		return nil, err
+	}
+	return h, nil
+}
+
+func (e *EDNS0_NSID) Option() uint16        { return EDNS0NSID }
+func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
+func (e *EDNS0_NSID) String() string        { return string(e.Nsid) }
+
+// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
+// an idea of where the client lives. It can then give back a different
+// answer depending on the location or network topology.
+// Basic use pattern for creating an subnet option:
+//
+//	o := new(dns.OPT)
+//	o.Hdr.Name = "."
+//	o.Hdr.Rrtype = dns.TypeOPT
+//	e := new(dns.EDNS0_SUBNET)
+//	e.Code = dns.EDNS0SUBNET
+//	e.Family = 1	// 1 for IPv4 source address, 2 for IPv6
+//	e.NetMask = 32	// 32 for IPV4, 128 for IPv6
+//	e.SourceScope = 0
+//	e.Address = net.ParseIP("127.0.0.1").To4()	// for IPv4
+//	// e.Address = net.ParseIP("2001:7b8:32a::2")	// for IPV6
+//	o.Option = append(o.Option, e)
+//
+// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic
+// for which netmask applies to the address. 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
+	DraftOption   bool // Set to true if using the old (0x50fa) option code
+}
+
+func (e *EDNS0_SUBNET) Option() uint16 {
+	if e.DraftOption {
+		return EDNS0SUBNETDRAFT
+	}
+	return EDNS0SUBNET
+}
+
+func (e *EDNS0_SUBNET) pack() ([]byte, error) {
+	b := make([]byte, 4)
+	b[0], b[1] = packUint16(e.Family)
+	b[2] = e.SourceNetmask
+	b[3] = e.SourceScope
+	switch e.Family {
+	case 1:
+		if e.SourceNetmask > net.IPv4len*8 {
+			return nil, errors.New("dns: bad netmask")
+		}
+		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, _ = unpackUint16(b, 0)
+	e.SourceNetmask = b[2]
+	e.SourceScope = b[3]
+	switch e.Family {
+	case 1:
+		if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
+			return errors.New("dns: bad netmask")
+		}
+		addr := make([]byte, net.IPv4len)
+		for i := 0; i < net.IPv4len && 4+i < len(b); i++ {
+			addr[i] = b[4+i]
+		}
+		e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3])
+	case 2:
+		if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
+			return errors.New("dns: bad netmask")
+		}
+		addr := make([]byte, net.IPv6len)
+		for i := 0; i < net.IPv6len && 4+i < len(b); i++ {
+			addr[i] = b[4+i]
+		}
+		e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4],
+			addr[5], addr[6], addr[7], addr[8], addr[9], addr[10],
+			addr[11], addr[12], addr[13], addr[14], addr[15]}
+	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
+}
+
+// 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
+}
+
+func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
+func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) }
+
+// Copied: http://golang.org/src/pkg/net/dnsmsg.go
+func (e *EDNS0_UL) pack() ([]byte, error) {
+	b := make([]byte, 4)
+	b[0] = byte(e.Lease >> 24)
+	b[1] = byte(e.Lease >> 16)
+	b[2] = byte(e.Lease >> 8)
+	b[3] = byte(e.Lease)
+	return b, nil
+}
+
+func (e *EDNS0_UL) unpack(b []byte) error {
+	if len(b) < 4 {
+		return ErrBuf
+	}
+	e.Lease = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
+	return nil
+}
+
+// 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
+}
+
+func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
+
+func (e *EDNS0_LLQ) pack() ([]byte, error) {
+	b := make([]byte, 18)
+	b[0], b[1] = packUint16(e.Version)
+	b[2], b[3] = packUint16(e.Opcode)
+	b[4], b[5] = packUint16(e.Error)
+	b[6] = byte(e.Id >> 56)
+	b[7] = byte(e.Id >> 48)
+	b[8] = byte(e.Id >> 40)
+	b[9] = byte(e.Id >> 32)
+	b[10] = byte(e.Id >> 24)
+	b[11] = byte(e.Id >> 16)
+	b[12] = byte(e.Id >> 8)
+	b[13] = byte(e.Id)
+	b[14] = byte(e.LeaseLife >> 24)
+	b[15] = byte(e.LeaseLife >> 16)
+	b[16] = byte(e.LeaseLife >> 8)
+	b[17] = byte(e.LeaseLife)
+	return b, nil
+}
+
+func (e *EDNS0_LLQ) unpack(b []byte) error {
+	if len(b) < 18 {
+		return ErrBuf
+	}
+	e.Version, _ = unpackUint16(b, 0)
+	e.Opcode, _ = unpackUint16(b, 2)
+	e.Error, _ = unpackUint16(b, 4)
+	e.Id = uint64(b[6])<<56 | uint64(b[6+1])<<48 | uint64(b[6+2])<<40 |
+		uint64(b[6+3])<<32 | uint64(b[6+4])<<24 | uint64(b[6+5])<<16 | uint64(b[6+6])<<8 | uint64(b[6+7])
+	e.LeaseLife = uint32(b[14])<<24 | uint32(b[14+1])<<16 | uint32(b[14+2])<<8 | uint32(b[14+3])
+	return nil
+}
+
+func (e *EDNS0_LLQ) String() string {
+	s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
+		" " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(uint64(e.Id), 10) +
+		" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
+	return s
+}
+
+type EDNS0_DAU struct {
+	Code    uint16 // Always EDNS0DAU
+	AlgCode []uint8
+}
+
+func (e *EDNS0_DAU) Option() uint16        { return EDNS0DAU }
+func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
+func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
+
+func (e *EDNS0_DAU) String() string {
+	s := ""
+	for i := 0; i < len(e.AlgCode); i++ {
+		if a, ok := AlgorithmToString[e.AlgCode[i]]; ok {
+			s += " " + a
+		} else {
+			s += " " + strconv.Itoa(int(e.AlgCode[i]))
+		}
+	}
+	return s
+}
+
+type EDNS0_DHU struct {
+	Code    uint16 // Always EDNS0DHU
+	AlgCode []uint8
+}
+
+func (e *EDNS0_DHU) Option() uint16        { return EDNS0DHU }
+func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
+func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
+
+func (e *EDNS0_DHU) String() string {
+	s := ""
+	for i := 0; i < len(e.AlgCode); i++ {
+		if a, ok := HashToString[e.AlgCode[i]]; ok {
+			s += " " + a
+		} else {
+			s += " " + strconv.Itoa(int(e.AlgCode[i]))
+		}
+	}
+	return s
+}
+
+type EDNS0_N3U struct {
+	Code    uint16 // Always EDNS0N3U
+	AlgCode []uint8
+}
+
+func (e *EDNS0_N3U) Option() uint16        { return EDNS0N3U }
+func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
+func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
+
+func (e *EDNS0_N3U) String() string {
+	// Re-use the hash map
+	s := ""
+	for i := 0; i < len(e.AlgCode); i++ {
+		if a, ok := HashToString[e.AlgCode[i]]; ok {
+			s += " " + a
+		} else {
+			s += " " + strconv.Itoa(int(e.AlgCode[i]))
+		}
+	}
+	return s
+}
+
+type EDNS0_EXPIRE struct {
+	Code   uint16 // Always EDNS0EXPIRE
+	Expire uint32
+}
+
+func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
+func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
+
+func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
+	b := make([]byte, 4)
+	b[0] = byte(e.Expire >> 24)
+	b[1] = byte(e.Expire >> 16)
+	b[2] = byte(e.Expire >> 8)
+	b[3] = byte(e.Expire)
+	return b, nil
+}
+
+func (e *EDNS0_EXPIRE) unpack(b []byte) error {
+	if len(b) < 4 {
+		return ErrBuf
+	}
+	e.Expire = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
+	return nil
+}
+
+// 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
+}
+
+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) 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
+}

+ 96 - 0
vendor/src/github.com/miekg/dns/format.go

@@ -0,0 +1,96 @@
+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 k := d.Kind(); k {
+	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
+		case `dns:"wks"`:
+			if d.Len() == 0 {
+				return ""
+			}
+			s := strconv.Itoa(int(d.Index(0).Uint()))
+			for i := 0; i < d.Len(); i++ {
+				s += " " + strconv.Itoa(int(d.Index(i).Uint()))
+			}
+			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 ""
+}

+ 162 - 0
vendor/src/github.com/miekg/dns/labels.go

@@ -0,0 +1,162 @@
+package dns
+
+// Holds a bunch of helper functions for dealing with labels.
+
+// SplitDomainName splits a name string into it's labels.
+// www.miek.nl. returns []string{"www", "miek", "nl"}
+// The root label (.) returns nil. Note that using
+// strings.Split(s) will work in most cases, but does not handle
+// escaped dots (\.) for instance.
+func SplitDomainName(s string) (labels []string) {
+	if len(s) == 0 {
+		return nil
+	}
+	fqdnEnd := 0 // offset of the final '.' or the length of the name
+	idx := Split(s)
+	begin := 0
+	if s[len(s)-1] == '.' {
+		fqdnEnd = len(s) - 1
+	} else {
+		fqdnEnd = len(s)
+	}
+
+	switch len(idx) {
+	case 0:
+		return nil
+	case 1:
+		// no-op
+	default:
+		end := 0
+		for i := 1; i < len(idx); i++ {
+			end = idx[i]
+			labels = append(labels, s[begin:end-1])
+			begin = end
+		}
+	}
+
+	labels = append(labels, s[begin:fqdnEnd])
+	return labels
+}
+
+// CompareDomainName compares the names s1 and s2 and
+// returns how many labels they have in common starting from the *right*.
+// The comparison stops at the first inequality. The names are not downcased
+// before the comparison.
+//
+// www.miek.nl. and miek.nl. have two labels in common: miek and nl
+// www.miek.nl. and www.bla.nl. have one label in common: nl
+func CompareDomainName(s1, s2 string) (n int) {
+	s1 = Fqdn(s1)
+	s2 = Fqdn(s2)
+	l1 := Split(s1)
+	l2 := Split(s2)
+
+	// the first check: root label
+	if l1 == nil || l2 == nil {
+		return
+	}
+
+	j1 := len(l1) - 1 // end
+	i1 := len(l1) - 2 // start
+	j2 := len(l2) - 1
+	i2 := len(l2) - 2
+	// the second check can be done here: last/only label
+	// before we fall through into the for-loop below
+	if s1[l1[j1]:] == s2[l2[j2]:] {
+		n++
+	} else {
+		return
+	}
+	for {
+		if i1 < 0 || i2 < 0 {
+			break
+		}
+		if s1[l1[i1]:l1[j1]] == s2[l2[i2]:l2[j2]] {
+			n++
+		} else {
+			break
+		}
+		j1--
+		i1--
+		j2--
+		i2--
+	}
+	return
+}
+
+// CountLabel counts the the number of labels in the string s.
+func CountLabel(s string) (labels int) {
+	if s == "." {
+		return
+	}
+	off := 0
+	end := false
+	for {
+		off, end = NextLabel(s, off)
+		labels++
+		if end {
+			return
+		}
+	}
+}
+
+// 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.
+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
+}

+ 1945 - 0
vendor/src/github.com/miekg/dns/msg.go

@@ -0,0 +1,1945 @@
+// 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
+
+import (
+	"encoding/base32"
+	"encoding/base64"
+	"encoding/hex"
+	"math/big"
+	"math/rand"
+	"net"
+	"reflect"
+	"strconv"
+	"time"
+)
+
+const maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer
+
+var (
+	// ErrAlg indicates an error with the (DNSSEC) algorithm.
+	ErrAlg error = &Error{err: "bad algorithm"}
+	// ErrAuth indicates an error in the TSIG authentication.
+	ErrAuth error = &Error{err: "bad authentication"}
+	// ErrBuf indicates that the buffer used it too small for the message.
+	ErrBuf error = &Error{err: "buffer size too small"}
+	// ErrConnEmpty indicates a connection is being uses before it is initialized.
+	ErrConnEmpty error = &Error{err: "conn has no connection"}
+	// ErrExtendedRcode ...
+	ErrExtendedRcode error = &Error{err: "bad extended rcode"}
+	// ErrFqdn indicates that a domain name does not have a closing dot.
+	ErrFqdn error = &Error{err: "domain must be fully qualified"}
+	// ErrId indicates there is a mismatch with the message's ID.
+	ErrId error = &Error{err: "id mismatch"}
+	// ErrKeyAlg indicates that the algorithm in the key is not valid.
+	ErrKeyAlg    error = &Error{err: "bad key algorithm"}
+	ErrKey       error = &Error{err: "bad key"}
+	ErrKeySize   error = &Error{err: "bad key size"}
+	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 indicates that a signature can not be cryptographically validated.
+	ErrSig error = &Error{err: "bad signature"}
+	// ErrSOA indicates that no SOA RR was seen when doing zone transfers.
+	ErrSoa error = &Error{err: "no SOA"}
+	// ErrTime indicates a timing error in TSIG authentication.
+	ErrTime error = &Error{err: "bad time"}
+	// ErrTruncated indicates that we failed to unpack a truncated message.
+	// We unpacked as much as we had so Msg can still be used, if desired.
+	ErrTruncated error = &Error{err: "failed to unpack truncated message"}
+)
+
+// Id, by default, returns a 16 bits random number to be used as a
+// 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 func() uint16 = 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. This not part of the official DNS packet 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.
+}
+
+// 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)
+
+// Map of opcodes strings.
+var StringToOpcode = reverseInt(OpcodeToString)
+
+// Map of rcodes strings.
+var StringToRcode = reverseInt(RcodeToString)
+
+// 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: "NOTIMPL",
+	RcodeRefused:        "REFUSED",
+	RcodeYXDomain:       "YXDOMAIN", // From 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",
+}
+
+// Rather than write the usual handful of routines to pack and
+// unpack every message that can appear on the wire, we use
+// reflection to write a generic pack/unpack for structs and then
+// use it. Thus, if in the future we need to define new message
+// structs, no new pack/unpack/printing code needs to be written.
+
+// 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) {
+	off1, _, err = packDomainName(s, msg, off, compression, compress)
+	return
+}
+
+func packDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, labels int, err error) {
+	// special case if msg == nil
+	lenmsg := 256
+	if msg != nil {
+		lenmsg = len(msg)
+	}
+	ls := len(s)
+	if ls == 0 { // Ok, for instance when dealing with update RR without any rdata.
+		return off, 0, nil
+	}
+	// If not fully qualified, error out, but only if msg == nil #ugly
+	switch {
+	case msg == nil:
+		if s[ls-1] != '.' {
+			s += "."
+			ls++
+		}
+	case msg != nil:
+		if s[ls-1] != '.' {
+			return lenmsg, 0, 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
+	nameoffset := -1
+	pointer := -1
+	// Emit sequence of counted strings, chopping at dots.
+	begin := 0
+	bs := []byte(s)
+	roBs, bsFresh, escapedDot := s, true, false
+	for i := 0; i < ls; i++ {
+		if bs[i] == '\\' {
+			for j := i; j < ls-1; j++ {
+				bs[j] = bs[j+1]
+			}
+			ls--
+			if off+1 > lenmsg {
+				return lenmsg, labels, ErrBuf
+			}
+			// check for \DDD
+			if i+2 < ls && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
+				bs[i] = dddToByte(bs[i:])
+				for j := i + 1; j < ls-2; j++ {
+					bs[j] = bs[j+2]
+				}
+				ls -= 2
+			} else if bs[i] == 't' {
+				bs[i] = '\t'
+			} else if bs[i] == 'r' {
+				bs[i] = '\r'
+			} else if bs[i] == 'n' {
+				bs[i] = '\n'
+			}
+			escapedDot = bs[i] == '.'
+			bsFresh = false
+			continue
+		}
+
+		if bs[i] == '.' {
+			if i > 0 && bs[i-1] == '.' && !escapedDot {
+				// two dots back to back is not legal
+				return lenmsg, labels, ErrRdata
+			}
+			if i-begin >= 1<<6 { // top two bits of length must be clear
+				return lenmsg, labels, 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 > lenmsg {
+				return lenmsg, labels, ErrBuf
+			}
+			if msg != nil {
+				msg[off] = byte(i - begin)
+			}
+			offset := off
+			off++
+			for j := begin; j < i; j++ {
+				if off+1 > lenmsg {
+					return lenmsg, labels, ErrBuf
+				}
+				if msg != nil {
+					msg[off] = bs[j]
+				}
+				off++
+			}
+			if compress && !bsFresh {
+				roBs = string(bs)
+				bsFresh = true
+			}
+			// Dont try to compress '.'
+			if compress && roBs[begin:] != "." {
+				if p, ok := compression[roBs[begin:]]; !ok {
+					// Only offsets smaller than this can be used.
+					if offset < maxCompressionOffset {
+						compression[roBs[begin:]] = offset
+					}
+				} else {
+					// 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 pointer == -1 && compress {
+						pointer = p         // Where to point to
+						nameoffset = offset // Where to point from
+						break
+					}
+				}
+			}
+			labels++
+			begin = i + 1
+		}
+		escapedDot = false
+	}
+	// Root label is special
+	if len(bs) == 1 && bs[0] == '.' {
+		return off, labels, 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
+		// if msg == nil, we will never do compression
+		msg[nameoffset], msg[nameoffset+1] = packUint16(uint16(pointer ^ 0xC000))
+		off = nameoffset + 1
+		goto End
+	}
+	if msg != nil {
+		msg[off] = 0
+	}
+End:
+	off++
+	return off, labels, nil
+}
+
+// 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.
+func UnpackDomainName(msg []byte, off int) (string, int, error) {
+	s := make([]byte, 0, 64)
+	off1 := 0
+	lenmsg := len(msg)
+	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
+			}
+			for j := off; j < off+c; j++ {
+				switch b := msg[j]; b {
+				case '.', '(', ')', ';', ' ', '@':
+					fallthrough
+				case '"', '\\':
+					s = append(s, '\\', b)
+				case '\t':
+					s = append(s, '\\', 't')
+				case '\r':
+					s = append(s, '\\', 'r')
+				default:
+					if b < 32 || b >= 127 { // unprintable use \DDD
+						var buf [3]byte
+						bufs := strconv.AppendInt(buf[:0], int64(b), 10)
+						s = append(s, '\\')
+						for i := 0; i < 3-len(bufs); i++ {
+							s = append(s, '0')
+						}
+						for _, r := range bufs {
+							s = append(s, r)
+						}
+					} 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 > 10 {
+				return "", lenmsg, &Error{err: "too many compression pointers"}
+			}
+			off = (c^0xC0)<<8 | int(c1)
+		default:
+			// 0x80 and 0x40 are reserved
+			return "", lenmsg, ErrRdata
+		}
+	}
+	if ptr == 0 {
+		off1 = off
+	}
+	if len(s) == 0 {
+		s = []byte(".")
+	}
+	return string(s), off1, nil
+}
+
+func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) {
+	var err error
+	if len(txt) == 0 {
+		if offset >= len(msg) {
+			return offset, ErrBuf
+		}
+		msg[offset] = 0
+		return offset, nil
+	}
+	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, err
+}
+
+func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) {
+	lenByteOffset := offset
+	if offset >= len(msg) {
+		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 if bs[i] == 't' {
+				msg[offset] = '\t'
+			} else if bs[i] == 'r' {
+				msg[offset] = '\r'
+			} else if bs[i] == 'n' {
+				msg[offset] = '\n'
+			} 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) {
+		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 = unpackTxtString(msg, off)
+		if err == nil {
+			ss = append(ss, s)
+		}
+	}
+	return
+}
+
+func unpackTxtString(msg []byte, offset int) (string, int, error) {
+	if offset+1 > len(msg) {
+		return "", offset, &Error{err: "overflow unpacking txt"}
+	}
+	l := int(msg[offset])
+	if offset+l+1 > len(msg) {
+		return "", offset, &Error{err: "overflow unpacking txt"}
+	}
+	s := make([]byte, 0, l)
+	for _, b := range msg[offset+1 : offset+1+l] {
+		switch b {
+		case '"', '\\':
+			s = append(s, '\\', b)
+		case '\t':
+			s = append(s, `\t`...)
+		case '\r':
+			s = append(s, `\r`...)
+		case '\n':
+			s = append(s, `\n`...)
+		default:
+			if b < 32 || b > 127 { // unprintable
+				var buf [3]byte
+				bufs := strconv.AppendInt(buf[:0], int64(b), 10)
+				s = append(s, '\\')
+				for i := 0; i < 3-len(bufs); i++ {
+					s = append(s, '0')
+				}
+				for _, r := range bufs {
+					s = append(s, r)
+				}
+			} else {
+				s = append(s, b)
+			}
+		}
+	}
+	offset += 1 + l
+	return string(s), offset, nil
+}
+
+// Pack a reflect.StructValue into msg.  Struct members can only be uint8, uint16, uint32, string,
+// slices and other (often anonymous) structs.
+func packStructValue(val reflect.Value, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
+	var txtTmp []byte
+	lenmsg := len(msg)
+	numfield := val.NumField()
+	for i := 0; i < numfield; i++ {
+		typefield := val.Type().Field(i)
+		if typefield.Tag == `dns:"-"` {
+			continue
+		}
+		switch fv := val.Field(i); fv.Kind() {
+		default:
+			return lenmsg, &Error{err: "bad kind packing"}
+		case reflect.Interface:
+			// PrivateRR is the only RR implementation that has interface field.
+			// therefore it's expected that this interface would be PrivateRdata
+			switch data := fv.Interface().(type) {
+			case PrivateRdata:
+				n, err := data.Pack(msg[off:])
+				if err != nil {
+					return lenmsg, err
+				}
+				off += n
+			default:
+				return lenmsg, &Error{err: "bad kind interface packing"}
+			}
+		case reflect.Slice:
+			switch typefield.Tag {
+			default:
+				return lenmsg, &Error{"bad tag packing slice: " + typefield.Tag.Get("dns")}
+			case `dns:"domain-name"`:
+				for j := 0; j < val.Field(i).Len(); j++ {
+					element := val.Field(i).Index(j).String()
+					off, err = PackDomainName(element, msg, off, compression, false && compress)
+					if err != nil {
+						return lenmsg, err
+					}
+				}
+			case `dns:"txt"`:
+				if txtTmp == nil {
+					txtTmp = make([]byte, 256*4+1)
+				}
+				off, err = packTxt(fv.Interface().([]string), msg, off, txtTmp)
+				if err != nil {
+					return lenmsg, err
+				}
+			case `dns:"opt"`: // edns
+				for j := 0; j < val.Field(i).Len(); j++ {
+					element := val.Field(i).Index(j).Interface()
+					b, e := element.(EDNS0).pack()
+					if e != nil {
+						return lenmsg, &Error{err: "overflow packing opt"}
+					}
+					// Option code
+					msg[off], msg[off+1] = packUint16(element.(EDNS0).Option())
+					// Length
+					msg[off+2], msg[off+3] = packUint16(uint16(len(b)))
+					off += 4
+					if off+len(b) > lenmsg {
+						copy(msg[off:], b)
+						off = lenmsg
+						continue
+					}
+					// Actual data
+					copy(msg[off:off+len(b)], b)
+					off += len(b)
+				}
+			case `dns:"a"`:
+				if val.Type().String() == "dns.IPSECKEY" {
+					// Field(2) is GatewayType, must be 1
+					if val.Field(2).Uint() != 1 {
+						continue
+					}
+				}
+				// It must be a slice of 4, even if it is 16, we encode
+				// only the first 4
+				if off+net.IPv4len > lenmsg {
+					return lenmsg, &Error{err: "overflow packing a"}
+				}
+				switch fv.Len() {
+				case net.IPv6len:
+					msg[off] = byte(fv.Index(12).Uint())
+					msg[off+1] = byte(fv.Index(13).Uint())
+					msg[off+2] = byte(fv.Index(14).Uint())
+					msg[off+3] = byte(fv.Index(15).Uint())
+					off += net.IPv4len
+				case net.IPv4len:
+					msg[off] = byte(fv.Index(0).Uint())
+					msg[off+1] = byte(fv.Index(1).Uint())
+					msg[off+2] = byte(fv.Index(2).Uint())
+					msg[off+3] = byte(fv.Index(3).Uint())
+					off += net.IPv4len
+				case 0:
+					// Allowed, for dynamic updates
+				default:
+					return lenmsg, &Error{err: "overflow packing a"}
+				}
+			case `dns:"aaaa"`:
+				if val.Type().String() == "dns.IPSECKEY" {
+					// Field(2) is GatewayType, must be 2
+					if val.Field(2).Uint() != 2 {
+						continue
+					}
+				}
+				if fv.Len() == 0 {
+					break
+				}
+				if fv.Len() > net.IPv6len || off+fv.Len() > lenmsg {
+					return lenmsg, &Error{err: "overflow packing aaaa"}
+				}
+				for j := 0; j < net.IPv6len; j++ {
+					msg[off] = byte(fv.Index(j).Uint())
+					off++
+				}
+			case `dns:"wks"`:
+				// TODO(miek): this is wrong should be lenrd
+				if off == lenmsg {
+					break // dyn. updates
+				}
+				if val.Field(i).Len() == 0 {
+					break
+				}
+				off1 := off
+				for j := 0; j < val.Field(i).Len(); j++ {
+					serv := int(fv.Index(j).Uint())
+					if off+serv/8+1 > len(msg) {
+						return len(msg), &Error{err: "overflow packing wks"}
+					}
+					msg[off+serv/8] |= byte(1 << (7 - uint(serv%8)))
+					if off+serv/8+1 > off1 {
+						off1 = off + serv/8 + 1
+					}
+				}
+				off = off1
+			case `dns:"nsec"`: // NSEC/NSEC3
+				// This is the uint16 type bitmap
+				if val.Field(i).Len() == 0 {
+					// Do absolutely nothing
+					break
+				}
+				var lastwindow, lastlength uint16
+				for j := 0; j < val.Field(i).Len(); j++ {
+					t := uint16(fv.Index(j).Uint())
+					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
+			}
+		case reflect.Struct:
+			off, err = packStructValue(fv, msg, off, compression, compress)
+			if err != nil {
+				return lenmsg, err
+			}
+		case reflect.Uint8:
+			if off+1 > lenmsg {
+				return lenmsg, &Error{err: "overflow packing uint8"}
+			}
+			msg[off] = byte(fv.Uint())
+			off++
+		case reflect.Uint16:
+			if off+2 > lenmsg {
+				return lenmsg, &Error{err: "overflow packing uint16"}
+			}
+			i := fv.Uint()
+			msg[off] = byte(i >> 8)
+			msg[off+1] = byte(i)
+			off += 2
+		case reflect.Uint32:
+			if off+4 > lenmsg {
+				return lenmsg, &Error{err: "overflow packing uint32"}
+			}
+			i := fv.Uint()
+			msg[off] = byte(i >> 24)
+			msg[off+1] = byte(i >> 16)
+			msg[off+2] = byte(i >> 8)
+			msg[off+3] = byte(i)
+			off += 4
+		case reflect.Uint64:
+			switch typefield.Tag {
+			default:
+				if off+8 > lenmsg {
+					return lenmsg, &Error{err: "overflow packing uint64"}
+				}
+				i := fv.Uint()
+				msg[off] = byte(i >> 56)
+				msg[off+1] = byte(i >> 48)
+				msg[off+2] = byte(i >> 40)
+				msg[off+3] = byte(i >> 32)
+				msg[off+4] = byte(i >> 24)
+				msg[off+5] = byte(i >> 16)
+				msg[off+6] = byte(i >> 8)
+				msg[off+7] = byte(i)
+				off += 8
+			case `dns:"uint48"`:
+				// Used in TSIG, where it stops at 48 bits, so we discard the upper 16
+				if off+6 > lenmsg {
+					return lenmsg, &Error{err: "overflow packing uint64 as uint48"}
+				}
+				i := fv.Uint()
+				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
+			}
+		case reflect.String:
+			// There are multiple string encodings.
+			// The tag distinguishes ordinary strings from domain names.
+			s := fv.String()
+			switch typefield.Tag {
+			default:
+				return lenmsg, &Error{"bad tag packing string: " + typefield.Tag.Get("dns")}
+			case `dns:"base64"`:
+				b64, e := fromBase64([]byte(s))
+				if e != nil {
+					return lenmsg, e
+				}
+				copy(msg[off:off+len(b64)], b64)
+				off += len(b64)
+			case `dns:"domain-name"`:
+				if val.Type().String() == "dns.IPSECKEY" {
+					// Field(2) is GatewayType, 1 and 2 or used for addresses
+					x := val.Field(2).Uint()
+					if x == 1 || x == 2 {
+						continue
+					}
+				}
+				if off, err = PackDomainName(s, msg, off, compression, false && compress); err != nil {
+					return lenmsg, err
+				}
+			case `dns:"cdomain-name"`:
+				if off, err = PackDomainName(s, msg, off, compression, true && compress); err != nil {
+					return lenmsg, err
+				}
+			case `dns:"size-base32"`:
+				// This is purely for NSEC3 atm, the previous byte must
+				// holds the length of the encoded string. As NSEC3
+				// is only defined to SHA1, the hashlength is 20 (160 bits)
+				msg[off-1] = 20
+				fallthrough
+			case `dns:"base32"`:
+				b32, e := fromBase32([]byte(s))
+				if e != nil {
+					return lenmsg, e
+				}
+				copy(msg[off:off+len(b32)], b32)
+				off += len(b32)
+			case `dns:"size-hex"`:
+				fallthrough
+			case `dns:"hex"`:
+				// There is no length encoded here
+				h, e := hex.DecodeString(s)
+				if e != nil {
+					return lenmsg, e
+				}
+				if off+hex.DecodedLen(len(s)) > lenmsg {
+					return lenmsg, &Error{err: "overflow packing hex"}
+				}
+				copy(msg[off:off+hex.DecodedLen(len(s))], h)
+				off += hex.DecodedLen(len(s))
+			case `dns:"size"`:
+				// the size is already encoded in the RR, we can safely use the
+				// length of string. String is RAW (not encoded in hex, nor base64)
+				copy(msg[off:off+len(s)], s)
+				off += len(s)
+			case `dns:"octet"`:
+				bytesTmp := make([]byte, 256)
+				off, err = packOctetString(fv.String(), msg, off, bytesTmp)
+				if err != nil {
+					return lenmsg, err
+				}
+			case `dns:"txt"`:
+				fallthrough
+			case "":
+				if txtTmp == nil {
+					txtTmp = make([]byte, 256*4+1)
+				}
+				off, err = packTxtString(fv.String(), msg, off, txtTmp)
+				if err != nil {
+					return lenmsg, err
+				}
+			}
+		}
+	}
+	return off, nil
+}
+
+func structValue(any interface{}) reflect.Value {
+	return reflect.ValueOf(any).Elem()
+}
+
+// PackStruct packs any structure to wire format.
+func PackStruct(any interface{}, msg []byte, off int) (off1 int, err error) {
+	off, err = packStructValue(structValue(any), msg, off, nil, false)
+	return off, err
+}
+
+func packStructCompress(any interface{}, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
+	off, err = packStructValue(structValue(any), msg, off, compression, compress)
+	return off, err
+}
+
+// Unpack a reflect.StructValue from msg.
+// Same restrictions as packStructValue.
+func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err error) {
+	lenmsg := len(msg)
+	for i := 0; i < val.NumField(); i++ {
+		if off > lenmsg {
+			return lenmsg, &Error{"bad offset unpacking"}
+		}
+		switch fv := val.Field(i); fv.Kind() {
+		default:
+			return lenmsg, &Error{err: "bad kind unpacking"}
+		case reflect.Interface:
+			// PrivateRR is the only RR implementation that has interface field.
+			// therefore it's expected that this interface would be PrivateRdata
+			switch data := fv.Interface().(type) {
+			case PrivateRdata:
+				n, err := data.Unpack(msg[off:])
+				if err != nil {
+					return lenmsg, err
+				}
+				off += n
+			default:
+				return lenmsg, &Error{err: "bad kind interface unpacking"}
+			}
+		case reflect.Slice:
+			switch val.Type().Field(i).Tag {
+			default:
+				return lenmsg, &Error{"bad tag unpacking slice: " + val.Type().Field(i).Tag.Get("dns")}
+			case `dns:"domain-name"`:
+				// HIP record slice of name (or none)
+				var servers []string
+				var s string
+				for off < lenmsg {
+					s, off, err = UnpackDomainName(msg, off)
+					if err != nil {
+						return lenmsg, err
+					}
+					servers = append(servers, s)
+				}
+				fv.Set(reflect.ValueOf(servers))
+			case `dns:"txt"`:
+				if off == lenmsg {
+					break
+				}
+				var txt []string
+				txt, off, err = unpackTxt(msg, off)
+				if err != nil {
+					return lenmsg, err
+				}
+				fv.Set(reflect.ValueOf(txt))
+			case `dns:"opt"`: // edns0
+				if off == lenmsg {
+					// This is an EDNS0 (OPT Record) with no rdata
+					// We can safely return here.
+					break
+				}
+				var edns []EDNS0
+			Option:
+				code := uint16(0)
+				if off+4 > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking opt"}
+				}
+				code, off = unpackUint16(msg, off)
+				optlen, off1 := unpackUint16(msg, off)
+				if off1+int(optlen) > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking opt"}
+				}
+				switch code {
+				case EDNS0NSID:
+					e := new(EDNS0_NSID)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				case EDNS0SUBNET, EDNS0SUBNETDRAFT:
+					e := new(EDNS0_SUBNET)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+					if code == EDNS0SUBNETDRAFT {
+						e.DraftOption = true
+					}
+				case EDNS0UL:
+					e := new(EDNS0_UL)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				case EDNS0LLQ:
+					e := new(EDNS0_LLQ)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				case EDNS0DAU:
+					e := new(EDNS0_DAU)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				case EDNS0DHU:
+					e := new(EDNS0_DHU)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				case EDNS0N3U:
+					e := new(EDNS0_N3U)
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				default:
+					e := new(EDNS0_LOCAL)
+					e.Code = code
+					if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
+						return lenmsg, err
+					}
+					edns = append(edns, e)
+					off = off1 + int(optlen)
+				}
+				if off < lenmsg {
+					goto Option
+				}
+				fv.Set(reflect.ValueOf(edns))
+			case `dns:"a"`:
+				if val.Type().String() == "dns.IPSECKEY" {
+					// Field(2) is GatewayType, must be 1
+					if val.Field(2).Uint() != 1 {
+						continue
+					}
+				}
+				if off == lenmsg {
+					break // dyn. update
+				}
+				if off+net.IPv4len > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking a"}
+				}
+				fv.Set(reflect.ValueOf(net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3])))
+				off += net.IPv4len
+			case `dns:"aaaa"`:
+				if val.Type().String() == "dns.IPSECKEY" {
+					// Field(2) is GatewayType, must be 2
+					if val.Field(2).Uint() != 2 {
+						continue
+					}
+				}
+				if off == lenmsg {
+					break
+				}
+				if off+net.IPv6len > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking aaaa"}
+				}
+				fv.Set(reflect.ValueOf(net.IP{msg[off], msg[off+1], msg[off+2], msg[off+3], msg[off+4],
+					msg[off+5], msg[off+6], msg[off+7], msg[off+8], msg[off+9], msg[off+10],
+					msg[off+11], msg[off+12], msg[off+13], msg[off+14], msg[off+15]}))
+				off += net.IPv6len
+			case `dns:"wks"`:
+				// Rest of the record is the bitmap
+				var serv []uint16
+				j := 0
+				for off < lenmsg {
+					if off+1 > lenmsg {
+						return lenmsg, &Error{err: "overflow unpacking wks"}
+					}
+					b := msg[off]
+					// Check the bits one by one, and set the type
+					if b&0x80 == 0x80 {
+						serv = append(serv, uint16(j*8+0))
+					}
+					if b&0x40 == 0x40 {
+						serv = append(serv, uint16(j*8+1))
+					}
+					if b&0x20 == 0x20 {
+						serv = append(serv, uint16(j*8+2))
+					}
+					if b&0x10 == 0x10 {
+						serv = append(serv, uint16(j*8+3))
+					}
+					if b&0x8 == 0x8 {
+						serv = append(serv, uint16(j*8+4))
+					}
+					if b&0x4 == 0x4 {
+						serv = append(serv, uint16(j*8+5))
+					}
+					if b&0x2 == 0x2 {
+						serv = append(serv, uint16(j*8+6))
+					}
+					if b&0x1 == 0x1 {
+						serv = append(serv, uint16(j*8+7))
+					}
+					j++
+					off++
+				}
+				fv.Set(reflect.ValueOf(serv))
+			case `dns:"nsec"`: // NSEC/NSEC3
+				if off == len(msg) {
+					break
+				}
+				// Rest of the record is the type bitmap
+				var nsec []uint16
+				length := 0
+				window := 0
+				lastwindow := -1
+				for off < len(msg) {
+					if off+2 > len(msg) {
+						return 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 len(msg), &Error{err: "out of order NSEC block"}
+					}
+					if length == 0 {
+						// RFC 4034: Blocks with no types present MUST NOT be included.
+						return len(msg), &Error{err: "empty NSEC block"}
+					}
+					if length > 32 {
+						return len(msg), &Error{err: "NSEC block too long"}
+					}
+					if off+length > len(msg) {
+						return 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
+				}
+				fv.Set(reflect.ValueOf(nsec))
+			}
+		case reflect.Struct:
+			off, err = unpackStructValue(fv, msg, off)
+			if err != nil {
+				return lenmsg, err
+			}
+			if val.Type().Field(i).Name == "Hdr" {
+				lenrd := off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint())
+				if lenrd > lenmsg {
+					return lenmsg, &Error{err: "overflowing header size"}
+				}
+				msg = msg[:lenrd]
+				lenmsg = len(msg)
+			}
+		case reflect.Uint8:
+			if off == lenmsg {
+				break
+			}
+			if off+1 > lenmsg {
+				return lenmsg, &Error{err: "overflow unpacking uint8"}
+			}
+			fv.SetUint(uint64(uint8(msg[off])))
+			off++
+		case reflect.Uint16:
+			if off == lenmsg {
+				break
+			}
+			var i uint16
+			if off+2 > lenmsg {
+				return lenmsg, &Error{err: "overflow unpacking uint16"}
+			}
+			i, off = unpackUint16(msg, off)
+			fv.SetUint(uint64(i))
+		case reflect.Uint32:
+			if off == lenmsg {
+				break
+			}
+			if off+4 > lenmsg {
+				return lenmsg, &Error{err: "overflow unpacking uint32"}
+			}
+			fv.SetUint(uint64(uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3])))
+			off += 4
+		case reflect.Uint64:
+			if off == lenmsg {
+				break
+			}
+			switch val.Type().Field(i).Tag {
+			default:
+				if off+8 > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking uint64"}
+				}
+				fv.SetUint(uint64(uint64(msg[off])<<56 | uint64(msg[off+1])<<48 | uint64(msg[off+2])<<40 |
+					uint64(msg[off+3])<<32 | uint64(msg[off+4])<<24 | uint64(msg[off+5])<<16 | uint64(msg[off+6])<<8 | uint64(msg[off+7])))
+				off += 8
+			case `dns:"uint48"`:
+				// Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes)
+				if off+6 > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking uint64 as uint48"}
+				}
+				fv.SetUint(uint64(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
+			}
+		case reflect.String:
+			var s string
+			if off == lenmsg {
+				break
+			}
+			switch val.Type().Field(i).Tag {
+			default:
+				return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")}
+			case `dns:"octet"`:
+				s = string(msg[off:])
+				off = lenmsg
+			case `dns:"hex"`:
+				hexend := lenmsg
+				if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) {
+					hexend = off + int(val.FieldByName("HitLength").Uint())
+				}
+				if hexend > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking HIP hex"}
+				}
+				s = hex.EncodeToString(msg[off:hexend])
+				off = hexend
+			case `dns:"base64"`:
+				// Rest of the RR is base64 encoded value
+				b64end := lenmsg
+				if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) {
+					b64end = off + int(val.FieldByName("PublicKeyLength").Uint())
+				}
+				if b64end > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking HIP base64"}
+				}
+				s = toBase64(msg[off:b64end])
+				off = b64end
+			case `dns:"cdomain-name"`:
+				fallthrough
+			case `dns:"domain-name"`:
+				if val.Type().String() == "dns.IPSECKEY" {
+					// Field(2) is GatewayType, 1 and 2 or used for addresses
+					x := val.Field(2).Uint()
+					if x == 1 || x == 2 {
+						continue
+					}
+				}
+				if off == lenmsg && int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) == 0 {
+					// zero rdata is ok for dyn updates, but only if rdlength is 0
+					break
+				}
+				s, off, err = UnpackDomainName(msg, off)
+				if err != nil {
+					return lenmsg, err
+				}
+			case `dns:"size-base32"`:
+				var size int
+				switch val.Type().Name() {
+				case "NSEC3":
+					switch val.Type().Field(i).Name {
+					case "NextDomain":
+						name := val.FieldByName("HashLength")
+						size = int(name.Uint())
+					}
+				}
+				if off+size > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking base32"}
+				}
+				s = toBase32(msg[off : off+size])
+				off += size
+			case `dns:"size-hex"`:
+				// a "size" string, but it must be encoded in hex in the string
+				var size int
+				switch val.Type().Name() {
+				case "NSEC3":
+					switch val.Type().Field(i).Name {
+					case "Salt":
+						name := val.FieldByName("SaltLength")
+						size = int(name.Uint())
+					case "NextDomain":
+						name := val.FieldByName("HashLength")
+						size = int(name.Uint())
+					}
+				case "TSIG":
+					switch val.Type().Field(i).Name {
+					case "MAC":
+						name := val.FieldByName("MACSize")
+						size = int(name.Uint())
+					case "OtherData":
+						name := val.FieldByName("OtherLen")
+						size = int(name.Uint())
+					}
+				}
+				if off+size > lenmsg {
+					return lenmsg, &Error{err: "overflow unpacking hex"}
+				}
+				s = hex.EncodeToString(msg[off : off+size])
+				off += size
+			case `dns:"txt"`:
+				fallthrough
+			case "":
+				s, off, err = unpackTxtString(msg, off)
+			}
+			fv.SetString(s)
+		}
+	}
+	return off, nil
+}
+
+// Helpers for dealing with escaped bytes
+func isDigit(b byte) bool { return b >= '0' && b <= '9' }
+
+func dddToByte(s []byte) byte {
+	return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
+}
+
+// UnpackStruct unpacks a binary message from offset off to the interface
+// value given.
+func UnpackStruct(any interface{}, msg []byte, off int) (int, error) {
+	return unpackStructValue(structValue(any), msg, off)
+}
+
+// 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
+}
+
+func unpackUint16(msg []byte, off int) (uint16, int) {
+	return uint16(msg[off])<<8 | uint16(msg[off+1]), off + 2
+}
+
+func packUint16(i uint16) (byte, byte) {
+	return byte(i >> 8), byte(i)
+}
+
+func toBase32(b []byte) string {
+	return base32.HexEncoding.EncodeToString(b)
+}
+
+func fromBase32(s []byte) (buf []byte, err error) {
+	buflen := base32.HexEncoding.DecodedLen(len(s))
+	buf = make([]byte, buflen)
+	n, err := base32.HexEncoding.Decode(buf, s)
+	buf = buf[:n]
+	return
+}
+
+func toBase64(b []byte) string {
+	return base64.StdEncoding.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
+}
+
+// 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) {
+	if rr == nil {
+		return len(msg), &Error{err: "nil rr"}
+	}
+
+	off1, err = packStructCompress(rr, msg, off, compression, compress)
+	if err != nil {
+		return len(msg), err
+	}
+	if rawSetRdlength(msg, off, off1) {
+		return off1, nil
+	}
+	return off, ErrRdata
+}
+
+// UnpackRR unpacks msg[off:] into an RR.
+func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
+	// unpack just the header, to find the rr type and length
+	var h RR_Header
+	off0 := off
+	if off, err = UnpackStruct(&h, msg, off); err != nil {
+		return nil, len(msg), err
+	}
+	end := off + int(h.Rdlength)
+	// make an rr of that type and re-unpack.
+	mk, known := TypeToRR[h.Rrtype]
+	if !known {
+		rr = new(RFC3597)
+	} else {
+		rr = mk()
+	}
+	off, err = UnpackStruct(rr, msg, off0)
+	if off != end {
+		return &h, end, &Error{err: "bad rdlength"}
+	}
+	return rr, off, err
+}
+
+// unpackRRslice unpacks msg[off:] into an []RR.
+// If we cannot unpack the whole array, then it will return nil
+func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) {
+	var r RR
+	// Optimistically make dst be the length that was sent
+	dst := make([]RR, 0, l)
+	for i := 0; i < l; i++ {
+		off1 := off
+		r, off, err = UnpackRR(msg, off)
+		if err != nil {
+			off = len(msg)
+			break
+		}
+		// If offset does not increase anymore, l is a lie
+		if off1 == off {
+			l = i
+			break
+		}
+		dst = append(dst, r)
+	}
+	if err != nil && off == len(msg) {
+		dst = nil
+	}
+	return dst, off, err
+}
+
+// Reverse a map
+func reverseInt8(m map[uint8]string) map[string]uint8 {
+	n := make(map[string]uint8)
+	for u, s := range m {
+		n[s] = u
+	}
+	return n
+}
+
+func reverseInt16(m map[uint16]string) map[string]uint16 {
+	n := make(map[string]uint16)
+	for u, s := range m {
+		n[s] = u
+	}
+	return n
+}
+
+func reverseInt(m map[int]string) map[string]int {
+	n := make(map[string]int)
+	for u, s := range m {
+		n[s] = u
+	}
+	return n
+}
+
+// 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) {
+	var dh Header
+	var compression map[string]int
+	if dns.Compress {
+		compression = make(map[string]int) // Compression pointer mappings
+	}
+
+	if dns.Rcode < 0 || dns.Rcode > 0xFFF {
+		return nil, ErrRcode
+	}
+	if dns.Rcode > 0xF {
+		// Regular RCODE field is 4 bits
+		opt := dns.IsEdns0()
+		if opt == nil {
+			return nil, ErrExtendedRcode
+		}
+		opt.SetExtendedRcode(uint8(dns.Rcode >> 4))
+		dns.Rcode &= 0xF
+	}
+
+	// Convert convenient Msg into wire-like Header.
+	dh.Id = dns.Id
+	dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode)
+	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
+	}
+
+	// Prepare variable sized arrays.
+	question := dns.Question
+	answer := dns.Answer
+	ns := dns.Ns
+	extra := dns.Extra
+
+	dh.Qdcount = uint16(len(question))
+	dh.Ancount = uint16(len(answer))
+	dh.Nscount = uint16(len(ns))
+	dh.Arcount = uint16(len(extra))
+
+	// We need the uncompressed length here, because we first pack it and then compress it.
+	msg = buf
+	compress := dns.Compress
+	dns.Compress = false
+	if packLen := dns.Len() + 1; len(msg) < packLen {
+		msg = make([]byte, packLen)
+	}
+	dns.Compress = compress
+
+	// Pack it in: header and then the pieces.
+	off := 0
+	off, err = packStructCompress(&dh, msg, off, compression, dns.Compress)
+	if err != nil {
+		return nil, err
+	}
+	for i := 0; i < len(question); i++ {
+		off, err = packStructCompress(&question[i], msg, off, compression, dns.Compress)
+		if err != nil {
+			return nil, err
+		}
+	}
+	for i := 0; i < len(answer); i++ {
+		off, err = PackRR(answer[i], msg, off, compression, dns.Compress)
+		if err != nil {
+			return nil, err
+		}
+	}
+	for i := 0; i < len(ns); i++ {
+		off, err = PackRR(ns[i], msg, off, compression, dns.Compress)
+		if err != nil {
+			return nil, err
+		}
+	}
+	for i := 0; i < len(extra); i++ {
+		off, err = PackRR(extra[i], msg, off, compression, dns.Compress)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return msg[:off], nil
+}
+
+// Unpack unpacks a binary message to a Msg structure.
+func (dns *Msg) Unpack(msg []byte) (err error) {
+	// Header.
+	var dh Header
+	off := 0
+	if off, err = UnpackStruct(&dh, msg, off); err != nil {
+		return err
+	}
+	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
+	dns.AuthenticatedData = (dh.Bits & _AD) != 0
+	dns.CheckingDisabled = (dh.Bits & _CD) != 0
+	dns.Rcode = int(dh.Bits & 0xF)
+
+	// Optimistically use the count given to us in the header
+	dns.Question = make([]Question, 0, int(dh.Qdcount))
+
+	var q Question
+	for i := 0; i < int(dh.Qdcount); i++ {
+		off1 := off
+		off, err = UnpackStruct(&q, msg, off)
+		if err != nil {
+			// Even if Truncated is set, we only will set ErrTruncated if we
+			// actually got the questions
+			return err
+		}
+		if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
+			dh.Qdcount = uint16(i)
+			break
+		}
+		dns.Question = append(dns.Question, q)
+	}
+
+	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))
+	if off != len(msg) {
+		// TODO(miek) make this an error?
+		// use PackOpt to let people tell how detailed the error reporting should be?
+		// println("dns: extra bytes in dns packet", off, "<", len(msg))
+	} else if dns.Truncated {
+		// Whether we ran into a an error or not, we want to return that it
+		// was truncated
+		err = ErrTruncated
+	}
+	return err
+}
+
+// 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
+}
+
+// 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 {
+	// We always return one more than needed.
+	l := 12 // Message header is always 12 bytes
+	var compression map[string]int
+	if dns.Compress {
+		compression = make(map[string]int)
+	}
+	for i := 0; i < len(dns.Question); i++ {
+		l += dns.Question[i].len()
+		if dns.Compress {
+			compressionLenHelper(compression, dns.Question[i].Name)
+		}
+	}
+	for i := 0; i < len(dns.Answer); i++ {
+		l += dns.Answer[i].len()
+		if dns.Compress {
+			k, ok := compressionLenSearch(compression, dns.Answer[i].Header().Name)
+			if ok {
+				l += 1 - k
+			}
+			compressionLenHelper(compression, dns.Answer[i].Header().Name)
+			k, ok = compressionLenSearchType(compression, dns.Answer[i])
+			if ok {
+				l += 1 - k
+			}
+			compressionLenHelperType(compression, dns.Answer[i])
+		}
+	}
+	for i := 0; i < len(dns.Ns); i++ {
+		l += dns.Ns[i].len()
+		if dns.Compress {
+			k, ok := compressionLenSearch(compression, dns.Ns[i].Header().Name)
+			if ok {
+				l += 1 - k
+			}
+			compressionLenHelper(compression, dns.Ns[i].Header().Name)
+			k, ok = compressionLenSearchType(compression, dns.Ns[i])
+			if ok {
+				l += 1 - k
+			}
+			compressionLenHelperType(compression, dns.Ns[i])
+		}
+	}
+	for i := 0; i < len(dns.Extra); i++ {
+		l += dns.Extra[i].len()
+		if dns.Compress {
+			k, ok := compressionLenSearch(compression, dns.Extra[i].Header().Name)
+			if ok {
+				l += 1 - k
+			}
+			compressionLenHelper(compression, dns.Extra[i].Header().Name)
+			k, ok = compressionLenSearchType(compression, dns.Extra[i])
+			if ok {
+				l += 1 - k
+			}
+			compressionLenHelperType(compression, dns.Extra[i])
+		}
+	}
+	return l
+}
+
+// Put the parts of the name in the compression map.
+func compressionLenHelper(c map[string]int, s string) {
+	pref := ""
+	lbs := Split(s)
+	for j := len(lbs) - 1; j >= 0; j-- {
+		pref = s[lbs[j]:]
+		if _, ok := c[pref]; !ok {
+			c[pref] = len(pref)
+		}
+	}
+}
+
+// Look for each part in the compression map and returns its length,
+// keep on searching so we get the longest match.
+func compressionLenSearch(c map[string]int, s string) (int, bool) {
+	off := 0
+	end := false
+	if s == "" { // don't bork on bogus data
+		return 0, false
+	}
+	for {
+		if _, ok := c[s[off:]]; ok {
+			return len(s[off:]), true
+		}
+		if end {
+			break
+		}
+		off, end = NextLabel(s, off)
+	}
+	return 0, false
+}
+
+// TODO(miek): should add all types, because the all can be *used* for compression.
+func compressionLenHelperType(c map[string]int, r RR) {
+	switch x := r.(type) {
+	case *NS:
+		compressionLenHelper(c, x.Ns)
+	case *MX:
+		compressionLenHelper(c, x.Mx)
+	case *CNAME:
+		compressionLenHelper(c, x.Target)
+	case *PTR:
+		compressionLenHelper(c, x.Ptr)
+	case *SOA:
+		compressionLenHelper(c, x.Ns)
+		compressionLenHelper(c, x.Mbox)
+	case *MB:
+		compressionLenHelper(c, x.Mb)
+	case *MG:
+		compressionLenHelper(c, x.Mg)
+	case *MR:
+		compressionLenHelper(c, x.Mr)
+	case *MF:
+		compressionLenHelper(c, x.Mf)
+	case *MD:
+		compressionLenHelper(c, x.Md)
+	case *RT:
+		compressionLenHelper(c, x.Host)
+	case *MINFO:
+		compressionLenHelper(c, x.Rmail)
+		compressionLenHelper(c, x.Email)
+	case *AFSDB:
+		compressionLenHelper(c, x.Hostname)
+	}
+}
+
+// Only search on compressing these types.
+func compressionLenSearchType(c map[string]int, r RR) (int, bool) {
+	switch x := r.(type) {
+	case *NS:
+		return compressionLenSearch(c, x.Ns)
+	case *MX:
+		return compressionLenSearch(c, x.Mx)
+	case *CNAME:
+		return compressionLenSearch(c, x.Target)
+	case *PTR:
+		return compressionLenSearch(c, x.Ptr)
+	case *SOA:
+		k, ok := compressionLenSearch(c, x.Ns)
+		k1, ok1 := compressionLenSearch(c, x.Mbox)
+		if !ok && !ok1 {
+			return 0, false
+		}
+		return k + k1, true
+	case *MB:
+		return compressionLenSearch(c, x.Mb)
+	case *MG:
+		return compressionLenSearch(c, x.Mg)
+	case *MR:
+		return compressionLenSearch(c, x.Mr)
+	case *MF:
+		return compressionLenSearch(c, x.Mf)
+	case *MD:
+		return compressionLenSearch(c, x.Md)
+	case *RT:
+		return compressionLenSearch(c, x.Host)
+	case *MINFO:
+		k, ok := compressionLenSearch(c, x.Rmail)
+		k1, ok1 := compressionLenSearch(c, x.Email)
+		if !ok && !ok1 {
+			return 0, false
+		}
+		return k + k1, true
+	case *AFSDB:
+		return compressionLenSearch(c, x.Hostname)
+	}
+	return 0, false
+}
+
+// id returns a 16 bits random number to be used as a
+// message id. The random provided should be good enough.
+func id() uint16 {
+	return uint16(rand.Int()) ^ uint16(time.Now().Nanosecond())
+}
+
+// Copy returns a new RR which is a deep-copy of r.
+func Copy(r RR) RR {
+	r1 := r.copy()
+	return r1
+}
+
+// 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
+}

+ 112 - 0
vendor/src/github.com/miekg/dns/nsecx.go

@@ -0,0 +1,112 @@
+package dns
+
+import (
+	"crypto/sha1"
+	"hash"
+	"io"
+	"strings"
+)
+
+type saltWireFmt struct {
+	Salt string `dns:"size-hex"`
+}
+
+// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in
+// uppercase.
+func HashName(label string, ha uint8, iter uint16, salt string) string {
+	saltwire := new(saltWireFmt)
+	saltwire.Salt = salt
+	wire := make([]byte, DefaultMsgSize)
+	n, err := PackStruct(saltwire, wire, 0)
+	if err != nil {
+		return ""
+	}
+	wire = wire[:n]
+	name := make([]byte, 255)
+	off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false)
+	if err != nil {
+		return ""
+	}
+	name = name[:off]
+	var s hash.Hash
+	switch ha {
+	case SHA1:
+		s = sha1.New()
+	default:
+		return ""
+	}
+
+	// k = 0
+	name = append(name, wire...)
+	io.WriteString(s, string(name))
+	nsec3 := s.Sum(nil)
+	// k > 0
+	for k := uint16(0); k < iter; k++ {
+		s.Reset()
+		nsec3 = append(nsec3, wire...)
+		io.WriteString(s, string(nsec3))
+		nsec3 = s.Sum(nil)
+	}
+	return toBase32(nsec3)
+}
+
+// Denialer is an interface that should be implemented by types that are used to denial
+// answers in DNSSEC.
+type Denialer interface {
+	// Cover will check if the (unhashed) name is being covered by this NSEC or NSEC3.
+	Cover(name string) bool
+	// Match will check if the ownername matches the (unhashed) name for this NSEC3 or NSEC3.
+	Match(name string) bool
+}
+
+// Cover implements the Denialer interface.
+func (rr *NSEC) Cover(name string) bool {
+	return true
+}
+
+// Match implements the Denialer interface.
+func (rr *NSEC) Match(name string) bool {
+	return true
+}
+
+// Cover implements the Denialer interface.
+func (rr *NSEC3) Cover(name string) bool {
+	// FIXME(miek): check if the zones match
+	// FIXME(miek): check if we're not dealing with parent nsec3
+	hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
+	labels := Split(rr.Hdr.Name)
+	if len(labels) < 2 {
+		return false
+	}
+	hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the dot
+	if hash == rr.NextDomain {
+		return false // empty interval
+	}
+	if hash > rr.NextDomain { // last name, points to apex
+		// hname > hash
+		// hname > rr.NextDomain
+		// TODO(miek)
+	}
+	if hname <= hash {
+		return false
+	}
+	if hname >= rr.NextDomain {
+		return false
+	}
+	return true
+}
+
+// Match implements the Denialer interface.
+func (rr *NSEC3) Match(name string) bool {
+	// FIXME(miek): Check if we are in the same zone
+	hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt)
+	labels := Split(rr.Hdr.Name)
+	if len(labels) < 2 {
+		return false
+	}
+	hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the .
+	if hash == hname {
+		return true
+	}
+	return false
+}

+ 117 - 0
vendor/src/github.com/miekg/dns/privaterr.go

@@ -0,0 +1,117 @@
+package dns
+
+import (
+	"fmt"
+	"strings"
+)
+
+// PrivateRdata is an interface used for implementing "Private Use" RR types, see
+// RFC 6895. This allows one to experiment with new RR types, without requesting an
+// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
+type PrivateRdata interface {
+	// String returns the text presentaton of the Rdata of the Private RR.
+	String() string
+	// Parse parses the Rdata of the private RR.
+	Parse([]string) error
+	// Pack is used when packing a private RR into a buffer.
+	Pack([]byte) (int, error)
+	// Unpack is used when unpacking a private RR from a buffer.
+	// TODO(miek): diff. signature than Pack, see edns0.go for instance.
+	Unpack([]byte) (int, error)
+	// Copy copies the Rdata.
+	Copy(PrivateRdata) error
+	// Len returns the length in octets of the Rdata.
+	Len() int
+}
+
+// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
+// It mocks normal RRs and implements dns.RR interface.
+type PrivateRR struct {
+	Hdr  RR_Header
+	Data PrivateRdata
+}
+
+func mkPrivateRR(rrtype uint16) *PrivateRR {
+	// Panics if RR is not an instance of PrivateRR.
+	rrfunc, ok := TypeToRR[rrtype]
+	if !ok {
+		panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
+	}
+
+	anyrr := rrfunc()
+	switch rr := anyrr.(type) {
+	case *PrivateRR:
+		return rr
+	}
+	panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
+}
+
+// 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() int { return r.Hdr.len() + r.Data.Len() }
+func (r *PrivateRR) copy() RR {
+	// make new RR like this:
+	rr := mkPrivateRR(r.Hdr.Rrtype)
+	newh := r.Hdr.copyHeader()
+	rr.Hdr = *newh
+
+	err := r.Data.Copy(rr.Data)
+	if err != nil {
+		panic("dns: got value that could not be used to copy Private rdata")
+	}
+	return rr
+}
+
+// PrivateHandle registers a private resource record type. It requires
+// string and numeric representation of private RR type and generator function as argument.
+func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
+	rtypestr = strings.ToUpper(rtypestr)
+
+	TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
+	TypeToString[rtype] = rtypestr
+	StringToType[rtypestr] = rtype
+
+	setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+		rr := mkPrivateRR(h.Rrtype)
+		rr.Hdr = h
+
+		var l lex
+		text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
+	FETCH:
+		for {
+			// TODO(miek): we could also be returning _QUOTE, this might or might not
+			// be an issue (basically parsing TXT becomes hard)
+			switch l = <-c; l.value {
+			case zNewline, zEOF:
+				break FETCH
+			case zString:
+				text = append(text, l.token)
+			}
+		}
+
+		err := rr.Data.Parse(text)
+		if err != nil {
+			return nil, &ParseError{f, err.Error(), l}, ""
+		}
+
+		return rr, nil, ""
+	}
+
+	typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
+}
+
+// PrivateHandleRemove removes defenitions required to support private RR type.
+func PrivateHandleRemove(rtype uint16) {
+	rtypestr, ok := TypeToString[rtype]
+	if ok {
+		delete(TypeToRR, rtype)
+		delete(TypeToString, rtype)
+		delete(typeToparserFunc, rtype)
+		delete(StringToType, rtypestr)
+	}
+	return
+}

+ 95 - 0
vendor/src/github.com/miekg/dns/rawmsg.go

@@ -0,0 +1,95 @@
+package dns
+
+// These raw* functions do not use reflection, they directly set the values
+// in the buffer. There are faster than their reflection counterparts.
+
+// RawSetId sets the message id in buf.
+func rawSetId(msg []byte, i uint16) bool {
+	if len(msg) < 2 {
+		return false
+	}
+	msg[0], msg[1] = packUint16(i)
+	return true
+}
+
+// rawSetQuestionLen sets the length of the question section.
+func rawSetQuestionLen(msg []byte, i uint16) bool {
+	if len(msg) < 6 {
+		return false
+	}
+	msg[4], msg[5] = packUint16(i)
+	return true
+}
+
+// rawSetAnswerLen sets the lenght of the answer section.
+func rawSetAnswerLen(msg []byte, i uint16) bool {
+	if len(msg) < 8 {
+		return false
+	}
+	msg[6], msg[7] = packUint16(i)
+	return true
+}
+
+// rawSetsNsLen sets the lenght of the authority section.
+func rawSetNsLen(msg []byte, i uint16) bool {
+	if len(msg) < 10 {
+		return false
+	}
+	msg[8], msg[9] = packUint16(i)
+	return true
+}
+
+// rawSetExtraLen sets the lenght of the additional section.
+func rawSetExtraLen(msg []byte, i uint16) bool {
+	if len(msg) < 12 {
+		return false
+	}
+	msg[10], msg[11] = packUint16(i)
+	return true
+}
+
+// rawSetRdlength sets the rdlength in the header of
+// the RR. The offset 'off' must be positioned at the
+// start of the header of the RR, 'end' must be the
+// end of the RR.
+func rawSetRdlength(msg []byte, off, end int) bool {
+	l := len(msg)
+Loop:
+	for {
+		if off+1 > l {
+			return false
+		}
+		c := int(msg[off])
+		off++
+		switch c & 0xC0 {
+		case 0x00:
+			if c == 0x00 {
+				// End of the domainname
+				break Loop
+			}
+			if off+c > l {
+				return false
+			}
+			off += c
+
+		case 0xC0:
+			// pointer, next byte included, ends domainname
+			off++
+			break Loop
+		}
+	}
+	// The domainname has been seen, we at the start of the fixed part in the header.
+	// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
+	off += 2 + 2 + 4
+	if off+2 > l {
+		return false
+	}
+	//off+1 is the end of the header, 'end' is the end of the rr
+	//so 'end' - 'off+2' is the length of the rdata
+	rdatalen := end - (off + 2)
+	if rdatalen > 0xFFFF {
+		return false
+	}
+	msg[off], msg[off+1] = packUint16(uint16(rdatalen))
+	return true
+}

+ 84 - 0
vendor/src/github.com/miekg/dns/sanitize.go

@@ -0,0 +1,84 @@
+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 temporay. 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 _, ok := m[key]; ok {
+			// Shortest TTL wins.
+			if m[key].Header().Ttl > r.Header().Ttl {
+				m[key].Header().Ttl = r.Header().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])
+}

+ 43 - 0
vendor/src/github.com/miekg/dns/scanner.go

@@ -0,0 +1,43 @@
+package dns
+
+// Implement a simple scanner, return a byte stream from an io reader.
+
+import (
+	"bufio"
+	"io"
+	"text/scanner"
+)
+
+type scan struct {
+	src      *bufio.Reader
+	position scanner.Position
+	eof      bool // Have we just seen a eof
+}
+
+func scanInit(r io.Reader) *scan {
+	s := new(scan)
+	s.src = bufio.NewReader(r)
+	s.position.Line = 1
+	return s
+}
+
+// tokenText returns the next byte from the input
+func (s *scan) tokenText() (byte, error) {
+	c, err := s.src.ReadByte()
+	if err != nil {
+		return c, err
+	}
+	// delay the newline handling until the next token is delivered,
+	// fixes off-by-one errors when reporting a parse error.
+	if s.eof == true {
+		s.position.Line++
+		s.position.Column = 0
+		s.eof = false
+	}
+	if c == '\n' {
+		s.eof = true
+		return c, nil
+	}
+	s.position.Column++
+	return c, nil
+}

+ 687 - 0
vendor/src/github.com/miekg/dns/server.go

@@ -0,0 +1,687 @@
+// DNS server implementation.
+
+package dns
+
+import (
+	"bytes"
+	"io"
+	"net"
+	"sync"
+	"time"
+)
+
+// Maximum number of TCP queries before we close the socket.
+const maxTCPQueries = 128
+
+// Handler is implemented by any value that implements ServeDNS.
+type Handler interface {
+	ServeDNS(w ResponseWriter, r *Msg)
+}
+
+// A ResponseWriter interface is used by an DNS handler to
+// construct an DNS response.
+type ResponseWriter interface {
+	// LocalAddr returns the net.Addr of the server
+	LocalAddr() net.Addr
+	// RemoteAddr returns the net.Addr of the client that sent the current request.
+	RemoteAddr() net.Addr
+	// WriteMsg writes a reply back to the client.
+	WriteMsg(*Msg) error
+	// Write writes a raw buffer back to the client.
+	Write([]byte) (int, error)
+	// Close closes the connection.
+	Close() error
+	// TsigStatus returns the status of the Tsig.
+	TsigStatus() error
+	// TsigTimersOnly sets the tsig timers only boolean.
+	TsigTimersOnly(bool)
+	// Hijack lets the caller take over the connection.
+	// After a call to Hijack(), the DNS package will not do anything with the connection.
+	Hijack()
+}
+
+type response struct {
+	hijacked       bool // connection has been hijacked by handler
+	tsigStatus     error
+	tsigTimersOnly bool
+	tsigRequestMAC string
+	tsigSecret     map[string]string // the tsig secrets
+	udp            *net.UDPConn      // i/o connection if UDP was used
+	tcp            *net.TCPConn      // i/o connection if TCP was used
+	udpSession     *SessionUDP       // oob data to get egress interface right
+	remoteAddr     net.Addr          // address of the client
+	writer         Writer            // writer to output the raw DNS bits
+}
+
+// ServeMux is an DNS request multiplexer. It matches the
+// zone name of each incoming request against a list of
+// registered patterns add calls the handler for the pattern
+// that most closely matches the zone name. ServeMux is DNSSEC aware, meaning
+// that queries for the DS record are redirected to the parent zone (if that
+// is also registered), otherwise the child gets the query.
+// ServeMux is also safe for concurrent access from multiple goroutines.
+type ServeMux struct {
+	z map[string]Handler
+	m *sync.RWMutex
+}
+
+// NewServeMux allocates and returns a new ServeMux.
+func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} }
+
+// DefaultServeMux is the default ServeMux used by Serve.
+var DefaultServeMux = NewServeMux()
+
+// The HandlerFunc type is an adapter to allow the use of
+// ordinary functions as DNS handlers.  If f is a function
+// with the appropriate signature, HandlerFunc(f) is a
+// Handler object that calls f.
+type HandlerFunc func(ResponseWriter, *Msg)
+
+// ServeDNS calls f(w, r).
+func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
+	f(w, r)
+}
+
+// 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)
+}
+
+func failedHandler() Handler { return HandlerFunc(HandleFailed) }
+
+// ListenAndServe Starts a server on addresss and network speficied. Invoke handler
+// for incoming queries.
+func ListenAndServe(addr string, network string, handler Handler) error {
+	server := &Server{Addr: addr, Net: network, Handler: handler}
+	return server.ListenAndServe()
+}
+
+// ActivateAndServe activates a server with a listener from systemd,
+// l and p should not both be non-nil.
+// If both l and p are not nil only p will be used.
+// Invoke handler for incoming queries.
+func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error {
+	server := &Server{Listener: l, PacketConn: p, Handler: handler}
+	return server.ActivateAndServe()
+}
+
+func (mux *ServeMux) match(q string, t uint16) Handler {
+	mux.m.RLock()
+	defer mux.m.RUnlock()
+	var handler Handler
+	b := make([]byte, len(q)) // worst case, one label of length q
+	off := 0
+	end := false
+	for {
+		l := len(q[off:])
+		for i := 0; i < l; i++ {
+			b[i] = q[off+i]
+			if b[i] >= 'A' && b[i] <= 'Z' {
+				b[i] |= ('a' - 'A')
+			}
+		}
+		if h, ok := mux.z[string(b[:l])]; ok { // 'causes garbage, might want to change the map key
+			if t != TypeDS {
+				return h
+			}
+			// Continue for DS to see if we have a parent too, if so delegeate to the parent
+			handler = h
+		}
+		off, end = NextLabel(q, off)
+		if end {
+			break
+		}
+	}
+	// Wildcard match, if we have found nothing try the root zone as a last resort.
+	if h, ok := mux.z["."]; ok {
+		return h
+	}
+	return handler
+}
+
+// Handle adds a handler to the ServeMux for pattern.
+func (mux *ServeMux) Handle(pattern string, handler Handler) {
+	if pattern == "" {
+		panic("dns: invalid pattern " + pattern)
+	}
+	mux.m.Lock()
+	mux.z[Fqdn(pattern)] = handler
+	mux.m.Unlock()
+}
+
+// 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 deregistrars 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. If DefaultServeMux
+// is used the correct thing for DS queries is done: a possible parent
+// is sought.
+// If no handler is found a standard SERVFAIL message is returned
+// If the request message does not have exactly one question in the
+// question section a SERVFAIL is returned, unlesss Unsafe is true.
+func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
+	var h Handler
+	if len(request.Question) < 1 { // allow more than one question
+		h = failedHandler()
+	} else {
+		if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil {
+			h = failedHandler()
+		}
+	}
+	h.ServeDNS(w, request)
+}
+
+// Handle registers the handler with the given pattern
+// in the DefaultServeMux. The documentation for
+// ServeMux explains how patterns are matched.
+func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
+
+// HandleRemove deregisters the handle with the given pattern
+// in the DefaultServeMux.
+func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) }
+
+// HandleFunc registers the handler function with the given pattern
+// in the DefaultServeMux.
+func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
+	DefaultServeMux.HandleFunc(pattern, handler)
+}
+
+// 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.TCPConn, 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.TCPConn, 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" it will invoke a TCP listener, otherwise an UDP one.
+	Net string
+	// TCP Listener to use, this is to aid in systemd's socket activation.
+	Listener net.Listener
+	// UDP "Listener" to use, this is to aid in systemd's socket activation.
+	PacketConn net.PacketConn
+	// Handler to invoke, dns.DefaultServeMux if nil.
+	Handler Handler
+	// Default buffer size to use to read incoming UDP messages. If not set
+	// it defaults to MinMsgSize (512 B).
+	UDPSize int
+	// The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second.
+	ReadTimeout time.Duration
+	// The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second.
+	WriteTimeout time.Duration
+	// TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966).
+	IdleTimeout func() time.Duration
+	// Secret(s) for Tsig map[<zonename>]<base64 secret>.
+	TsigSecret map[string]string
+	// Unsafe instructs the server to disregard any sanity checks and directly hand the message to
+	// the handler. It will specfically not check if the query has the QR bit not set.
+	Unsafe bool
+	// If NotifyStartedFunc is set 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
+
+	// Graceful shutdown handling
+
+	inFlight sync.WaitGroup
+
+	lock    sync.RWMutex
+	started bool
+}
+
+// ListenAndServe starts a nameserver on the configured address in *Server.
+func (srv *Server) ListenAndServe() error {
+	srv.lock.Lock()
+	defer srv.lock.Unlock()
+	if srv.started {
+		return &Error{err: "server already started"}
+	}
+	addr := srv.Addr
+	if addr == "" {
+		addr = ":domain"
+	}
+	if srv.UDPSize == 0 {
+		srv.UDPSize = MinMsgSize
+	}
+	switch srv.Net {
+	case "tcp", "tcp4", "tcp6":
+		a, e := net.ResolveTCPAddr(srv.Net, addr)
+		if e != nil {
+			return e
+		}
+		l, e := net.ListenTCP(srv.Net, a)
+		if e != nil {
+			return e
+		}
+		srv.Listener = l
+		srv.started = true
+		srv.lock.Unlock()
+		e = srv.serveTCP(l)
+		srv.lock.Lock() // to satisfy the defer at the top
+		return e
+	case "udp", "udp4", "udp6":
+		a, e := net.ResolveUDPAddr(srv.Net, addr)
+		if e != nil {
+			return e
+		}
+		l, e := net.ListenUDP(srv.Net, a)
+		if e != nil {
+			return e
+		}
+		if e := setUDPSocketOptions(l); e != nil {
+			return e
+		}
+		srv.PacketConn = l
+		srv.started = true
+		srv.lock.Unlock()
+		e = srv.serveUDP(l)
+		srv.lock.Lock() // to satisfy the defer at the top
+		return e
+	}
+	return &Error{err: "bad network"}
+}
+
+// ActivateAndServe starts a nameserver with the PacketConn or Listener
+// configured in *Server. Its main use is to start a server from systemd.
+func (srv *Server) ActivateAndServe() error {
+	srv.lock.Lock()
+	defer srv.lock.Unlock()
+	if srv.started {
+		return &Error{err: "server already started"}
+	}
+	pConn := srv.PacketConn
+	l := srv.Listener
+	if pConn != nil {
+		if srv.UDPSize == 0 {
+			srv.UDPSize = MinMsgSize
+		}
+		if t, ok := pConn.(*net.UDPConn); ok {
+			if e := setUDPSocketOptions(t); e != nil {
+				return e
+			}
+			srv.started = true
+			srv.lock.Unlock()
+			e := srv.serveUDP(t)
+			srv.lock.Lock() // to satisfy the defer at the top
+			return e
+		}
+	}
+	if l != nil {
+		if t, ok := l.(*net.TCPListener); ok {
+			srv.started = true
+			srv.lock.Unlock()
+			e := srv.serveTCP(t)
+			srv.lock.Lock() // to satisfy the defer at the top
+			return e
+		}
+	}
+	return &Error{err: "bad listeners"}
+}
+
+// Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and
+// ActivateAndServe will return. All in progress queries are completed before the server
+// is taken down. If the Shutdown is taking longer than the reading timeout an error
+// is returned.
+func (srv *Server) Shutdown() error {
+	srv.lock.Lock()
+	if !srv.started {
+		srv.lock.Unlock()
+		return &Error{err: "server not started"}
+	}
+	srv.started = false
+	srv.lock.Unlock()
+
+	if srv.PacketConn != nil {
+		srv.PacketConn.Close()
+	}
+	if srv.Listener != nil {
+		srv.Listener.Close()
+	}
+
+	fin := make(chan bool)
+	go func() {
+		srv.inFlight.Wait()
+		fin <- true
+	}()
+
+	select {
+	case <-time.After(srv.getReadTimeout()):
+		return &Error{err: "server shutdown is pending"}
+	case <-fin:
+		return nil
+	}
+}
+
+// getReadTimeout is a helper func to use system timeout if server did not intend to change it.
+func (srv *Server) getReadTimeout() time.Duration {
+	rtimeout := dnsTimeout
+	if srv.ReadTimeout != 0 {
+		rtimeout = srv.ReadTimeout
+	}
+	return rtimeout
+}
+
+// serveTCP starts a TCP listener for the server.
+// Each request is handled in a separate goroutine.
+func (srv *Server) serveTCP(l *net.TCPListener) error {
+	defer l.Close()
+
+	if srv.NotifyStartedFunc != nil {
+		srv.NotifyStartedFunc()
+	}
+
+	reader := Reader(&defaultReader{srv})
+	if srv.DecorateReader != nil {
+		reader = srv.DecorateReader(reader)
+	}
+
+	handler := srv.Handler
+	if handler == nil {
+		handler = DefaultServeMux
+	}
+	rtimeout := srv.getReadTimeout()
+	// deadline is not used here
+	for {
+		rw, e := l.AcceptTCP()
+		if e != nil {
+			if neterr, ok := e.(net.Error); ok && neterr.Temporary() {
+				continue
+			}
+			return e
+		}
+		m, e := reader.ReadTCP(rw, rtimeout)
+		srv.lock.RLock()
+		if !srv.started {
+			srv.lock.RUnlock()
+			return nil
+		}
+		srv.lock.RUnlock()
+		if e != nil {
+			continue
+		}
+		srv.inFlight.Add(1)
+		go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
+	}
+}
+
+// serveUDP starts a UDP listener for the server.
+// Each request is handled in a separate goroutine.
+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)
+	}
+
+	handler := srv.Handler
+	if handler == nil {
+		handler = DefaultServeMux
+	}
+	rtimeout := srv.getReadTimeout()
+	// deadline is not used here
+	for {
+		m, s, e := reader.ReadUDP(l, rtimeout)
+		srv.lock.RLock()
+		if !srv.started {
+			srv.lock.RUnlock()
+			return nil
+		}
+		srv.lock.RUnlock()
+		if e != nil {
+			continue
+		}
+		srv.inFlight.Add(1)
+		go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
+	}
+}
+
+// Serve a new connection.
+func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t *net.TCPConn) {
+	defer srv.inFlight.Done()
+
+	w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
+	if srv.DecorateWriter != nil {
+		w.writer = srv.DecorateWriter(w)
+	} else {
+		w.writer = w
+	}
+
+	q := 0 // counter for the amount of TCP queries we get
+
+	reader := Reader(&defaultReader{srv})
+	if srv.DecorateReader != nil {
+		reader = srv.DecorateReader(reader)
+	}
+Redo:
+	req := new(Msg)
+	err := req.Unpack(m)
+	if err != nil { // Send a FormatError back
+		x := new(Msg)
+		x.SetRcodeFormatError(req)
+		w.WriteMsg(x)
+		goto Exit
+	}
+	if !srv.Unsafe && req.Response {
+		goto Exit
+	}
+
+	w.tsigStatus = nil
+	if w.tsigSecret != nil {
+		if t := req.IsTsig(); t != nil {
+			secret := t.Hdr.Name
+			if _, ok := w.tsigSecret[secret]; !ok {
+				w.tsigStatus = ErrKeyAlg
+			}
+			w.tsigStatus = TsigVerify(m, w.tsigSecret[secret], "", false)
+			w.tsigTimersOnly = false
+			w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC
+		}
+	}
+	h.ServeDNS(w, req) // Writes back to the client
+
+Exit:
+	// TODO(miek): make this number configurable?
+	if q > maxTCPQueries { // close socket after this many queries
+		w.Close()
+		return
+	}
+
+	if w.hijacked {
+		return // client calls Close()
+	}
+	if u != nil { // UDP, "close" and return
+		w.Close()
+		return
+	}
+	idleTimeout := tcpIdleTimeout
+	if srv.IdleTimeout != nil {
+		idleTimeout = srv.IdleTimeout()
+	}
+	m, e := reader.ReadTCP(w.tcp, idleTimeout)
+	if e == nil {
+		q++
+		goto Redo
+	}
+	w.Close()
+	return
+}
+
+func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) {
+	conn.SetReadDeadline(time.Now().Add(timeout))
+	l := make([]byte, 2)
+	n, err := conn.Read(l)
+	if err != nil || n != 2 {
+		if err != nil {
+			return nil, err
+		}
+		return nil, ErrShortRead
+	}
+	length, _ := unpackUint16(l, 0)
+	if length == 0 {
+		return nil, ErrShortRead
+	}
+	m := make([]byte, int(length))
+	n, err = conn.Read(m[:int(length)])
+	if err != nil || n == 0 {
+		if err != nil {
+			return nil, err
+		}
+		return nil, ErrShortRead
+	}
+	i := n
+	for i < int(length) {
+		j, err := conn.Read(m[i:int(length)])
+		if err != nil {
+			return nil, err
+		}
+		i += j
+	}
+	n = i
+	m = m[:n]
+	return m, nil
+}
+
+func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) {
+	conn.SetReadDeadline(time.Now().Add(timeout))
+	m := make([]byte, srv.UDPSize)
+	n, s, e := ReadFromSessionUDP(conn, m)
+	if e != nil || n == 0 {
+		if e != nil {
+			return nil, nil, e
+		}
+		return nil, nil, ErrShortRead
+	}
+	m = m[:n]
+	return m, s, nil
+}
+
+// WriteMsg implements the ResponseWriter.WriteMsg method.
+func (w *response) WriteMsg(m *Msg) (err error) {
+	var data []byte
+	if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check)
+		if t := m.IsTsig(); t != nil {
+			data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly)
+			if err != nil {
+				return err
+			}
+			_, err = w.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) {
+	switch {
+	case w.udp != nil:
+		n, err := WriteToSessionUDP(w.udp, m, w.udpSession)
+		return n, err
+	case w.tcp != nil:
+		lm := len(m)
+		if lm < 2 {
+			return 0, io.ErrShortBuffer
+		}
+		if lm > MaxMsgSize {
+			return 0, &Error{err: "message too large"}
+		}
+		l := make([]byte, 2, 2+lm)
+		l[0], l[1] = packUint16(uint16(lm))
+		m = append(l, m...)
+
+		n, err := io.Copy(w.tcp, bytes.NewReader(m))
+		return int(n), err
+	}
+	panic("not reached")
+}
+
+// LocalAddr implements the ResponseWriter.LocalAddr method.
+func (w *response) LocalAddr() net.Addr {
+	if w.tcp != nil {
+		return w.tcp.LocalAddr()
+	}
+	return w.udp.LocalAddr()
+}
+
+// RemoteAddr implements the ResponseWriter.RemoteAddr method.
+func (w *response) RemoteAddr() net.Addr { return w.remoteAddr }
+
+// TsigStatus implements the ResponseWriter.TsigStatus method.
+func (w *response) TsigStatus() error { return w.tsigStatus }
+
+// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method.
+func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b }
+
+// Hijack implements the ResponseWriter.Hijack method.
+func (w *response) Hijack() { w.hijacked = true }
+
+// Close implements the ResponseWriter.Close method
+func (w *response) Close() error {
+	// Can't close the udp conn, as that is actually the listener.
+	if w.tcp != nil {
+		e := w.tcp.Close()
+		w.tcp = nil
+		return e
+	}
+	return nil
+}

+ 216 - 0
vendor/src/github.com/miekg/dns/sig0.go

@@ -0,0 +1,216 @@
+package dns
+
+import (
+	"crypto"
+	"crypto/dsa"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"math/big"
+	"strings"
+	"time"
+)
+
+// Sign signs a dns.Msg. It fills the signature with the appropriate data.
+// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
+// and Expiration set.
+func (rr *SIG) Sign(k 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.Header().Rrtype = TypeSIG
+	rr.Header().Class = ClassANY
+	rr.Header().Ttl = 0
+	rr.Header().Name = "."
+	rr.OrigTtl = 0
+	rr.TypeCovered = 0
+	rr.Labels = 0
+
+	buf := make([]byte, m.Len()+rr.len())
+	mbuf, err := m.PackBuffer(buf)
+	if err != nil {
+		return nil, err
+	}
+	if &buf[0] != &mbuf[0] {
+		return nil, ErrBuf
+	}
+	off, err := PackRR(rr, buf, len(mbuf), nil, false)
+	if err != nil {
+		return nil, err
+	}
+	buf = buf[:off:cap(buf)]
+
+	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)
+	sig := string(signature)
+
+	buf = append(buf, sig...)
+	if len(buf) > int(^uint16(0)) {
+		return nil, ErrBuf
+	}
+	// Adjust sig data length
+	rdoff := len(mbuf) + 1 + 2 + 2 + 4
+	rdlen, _ := unpackUint16(buf, rdoff)
+	rdlen += uint16(len(sig))
+	buf[rdoff], buf[rdoff+1] = packUint16(rdlen)
+	// Adjust additional count
+	adc, _ := unpackUint16(buf, 10)
+	adc++
+	buf[10], buf[11] = packUint16(adc)
+	return buf, nil
+}
+
+// Verify validates the message buf using the key k.
+// It's assumed that buf is a valid message from which rr was unpacked.
+func (rr *SIG) Verify(k *KEY, buf []byte) error {
+	if k == nil {
+		return ErrKey
+	}
+	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
+		return ErrKey
+	}
+
+	var hash crypto.Hash
+	switch rr.Algorithm {
+	case DSA, RSASHA1:
+		hash = crypto.SHA1
+	case RSASHA256, ECDSAP256SHA256:
+		hash = crypto.SHA256
+	case ECDSAP384SHA384:
+		hash = crypto.SHA384
+	case RSASHA512:
+		hash = crypto.SHA512
+	default:
+		return ErrAlg
+	}
+	hasher := hash.New()
+
+	buflen := len(buf)
+	qdc, _ := unpackUint16(buf, 4)
+	anc, _ := unpackUint16(buf, 6)
+	auc, _ := unpackUint16(buf, 8)
+	adc, offset := unpackUint16(buf, 10)
+	var err error
+	for i := uint16(0); i < qdc && offset < buflen; i++ {
+		_, offset, err = UnpackDomainName(buf, offset)
+		if err != nil {
+			return err
+		}
+		// Skip past Type and Class
+		offset += 2 + 2
+	}
+	for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ {
+		_, offset, err = UnpackDomainName(buf, offset)
+		if err != nil {
+			return err
+		}
+		// Skip past Type, Class and TTL
+		offset += 2 + 2 + 4
+		if offset+1 >= buflen {
+			continue
+		}
+		var rdlen uint16
+		rdlen, offset = unpackUint16(buf, offset)
+		offset += int(rdlen)
+	}
+	if offset >= buflen {
+		return &Error{err: "overflowing unpacking signed message"}
+	}
+
+	// offset should be just prior to SIG
+	bodyend := offset
+	// owner name SHOULD be root
+	_, offset, err = UnpackDomainName(buf, offset)
+	if err != nil {
+		return err
+	}
+	// Skip Type, Class, TTL, RDLen
+	offset += 2 + 2 + 4 + 2
+	sigstart := offset
+	// Skip Type Covered, Algorithm, Labels, Original TTL
+	offset += 2 + 1 + 1 + 4
+	if offset+4+4 >= buflen {
+		return &Error{err: "overflow unpacking signed message"}
+	}
+	expire := uint32(buf[offset])<<24 | uint32(buf[offset+1])<<16 | uint32(buf[offset+2])<<8 | uint32(buf[offset+3])
+	offset += 4
+	incept := uint32(buf[offset])<<24 | uint32(buf[offset+1])<<16 | uint32(buf[offset+2])<<8 | uint32(buf[offset+3])
+	offset += 4
+	now := uint32(time.Now().Unix())
+	if now < incept || now > expire {
+		return ErrTime
+	}
+	// Skip key tag
+	offset += 2
+	var signername string
+	signername, offset, err = UnpackDomainName(buf, offset)
+	if err != nil {
+		return err
+	}
+	// If key has come from the DNS name compression might
+	// have mangled the case of the name
+	if strings.ToLower(signername) != strings.ToLower(k.Header().Name) {
+		return &Error{err: "signer name doesn't match key name"}
+	}
+	sigend := offset
+	hasher.Write(buf[sigstart:sigend])
+	hasher.Write(buf[:10])
+	hasher.Write([]byte{
+		byte((adc - 1) << 8),
+		byte(adc - 1),
+	})
+	hasher.Write(buf[12:bodyend])
+
+	hashed := hasher.Sum(nil)
+	sig := buf[sigend:]
+	switch k.Algorithm {
+	case DSA:
+		pk := k.publicKeyDSA()
+		sig = sig[1:]
+		r := big.NewInt(0)
+		r.SetBytes(sig[:len(sig)/2])
+		s := big.NewInt(0)
+		s.SetBytes(sig[len(sig)/2:])
+		if pk != nil {
+			if dsa.Verify(pk, hashed, r, s) {
+				return nil
+			}
+			return ErrSig
+		}
+	case RSASHA1, RSASHA256, RSASHA512:
+		pk := k.publicKeyRSA()
+		if pk != nil {
+			return rsa.VerifyPKCS1v15(pk, hash, hashed, sig)
+		}
+	case ECDSAP256SHA256, ECDSAP384SHA384:
+		pk := k.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
+}

+ 57 - 0
vendor/src/github.com/miekg/dns/singleinflight.go

@@ -0,0 +1,57 @@
+// Copyright 2013 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Adapted for dns package usage by Miek Gieben.
+
+package dns
+
+import "sync"
+import "time"
+
+// call is an in-flight or completed singleflight.Do call
+type call struct {
+	wg   sync.WaitGroup
+	val  *Msg
+	rtt  time.Duration
+	err  error
+	dups int
+}
+
+// singleflight represents a class of work and forms a namespace in
+// which units of work can be executed with duplicate suppression.
+type singleflight struct {
+	sync.Mutex                  // protects m
+	m          map[string]*call // lazily initialized
+}
+
+// Do executes and returns the results of the given function, making
+// sure that only one execution is in-flight for a given key at a
+// time. If a duplicate comes in, the duplicate caller waits for the
+// original to complete and receives the same results.
+// The return value shared indicates whether v was given to multiple callers.
+func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) {
+	g.Lock()
+	if g.m == nil {
+		g.m = make(map[string]*call)
+	}
+	if c, ok := g.m[key]; ok {
+		c.dups++
+		g.Unlock()
+		c.wg.Wait()
+		return c.val, c.rtt, c.err, true
+	}
+	c := new(call)
+	c.wg.Add(1)
+	g.m[key] = c
+	g.Unlock()
+
+	c.val, c.rtt, c.err = fn()
+	c.wg.Done()
+
+	g.Lock()
+	delete(g.m, key)
+	g.Unlock()
+
+	return c.val, c.rtt, c.err, c.dups > 0
+}

+ 86 - 0
vendor/src/github.com/miekg/dns/tlsa.go

@@ -0,0 +1,86 @@
+package dns
+
+import (
+	"crypto/sha256"
+	"crypto/sha512"
+	"crypto/x509"
+	"encoding/hex"
+	"errors"
+	"io"
+	"net"
+	"strconv"
+)
+
+// CertificateToDANE converts a certificate to a hex string as used in the TLSA record.
+func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {
+	switch matchingType {
+	case 0:
+		switch selector {
+		case 0:
+			return hex.EncodeToString(cert.Raw), nil
+		case 1:
+			return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil
+		}
+	case 1:
+		h := sha256.New()
+		switch selector {
+		case 0:
+			io.WriteString(h, string(cert.Raw))
+			return hex.EncodeToString(h.Sum(nil)), nil
+		case 1:
+			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
+			return hex.EncodeToString(h.Sum(nil)), nil
+		}
+	case 2:
+		h := sha512.New()
+		switch selector {
+		case 0:
+			io.WriteString(h, string(cert.Raw))
+			return hex.EncodeToString(h.Sum(nil)), nil
+		case 1:
+			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
+			return hex.EncodeToString(h.Sum(nil)), nil
+		}
+	}
+	return "", errors.New("dns: bad TLSA MatchingType or TLSA Selector")
+}
+
+// Sign creates a TLSA record from an SSL certificate.
+func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) {
+	r.Hdr.Rrtype = TypeTLSA
+	r.Usage = uint8(usage)
+	r.Selector = uint8(selector)
+	r.MatchingType = uint8(matchingType)
+
+	r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Verify verifies a TLSA record against an SSL certificate. If it is OK
+// a nil error is returned.
+func (r *TLSA) Verify(cert *x509.Certificate) error {
+	c, err := CertificateToDANE(r.Selector, r.MatchingType, cert)
+	if err != nil {
+		return err // Not also ErrSig?
+	}
+	if r.Certificate == c {
+		return nil
+	}
+	return ErrSig // ErrSig, really?
+}
+
+// TLSAName returns the ownername of a TLSA resource record as per the
+// rules specified in RFC 6698, Section 3.
+func TLSAName(name, service, network string) (string, error) {
+	if !IsFqdn(name) {
+		return "", ErrFqdn
+	}
+	p, e := net.LookupPort(network, service)
+	if e != nil {
+		return "", e
+	}
+	return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil
+}

+ 320 - 0
vendor/src/github.com/miekg/dns/tsig.go

@@ -0,0 +1,320 @@
+package dns
+
+import (
+	"crypto/hmac"
+	"crypto/md5"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/sha512"
+	"encoding/hex"
+	"hash"
+	"io"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// HMAC hashing codes. These are transmitted as domain names.
+const (
+	HmacMD5    = "hmac-md5.sig-alg.reg.int."
+	HmacSHA1   = "hmac-sha1."
+	HmacSHA256 = "hmac-sha256."
+	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"`
+	OrigId     uint16
+	Error      uint16
+	OtherLen   uint16
+	OtherData  string `dns:"size-hex"`
+}
+
+// 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
+}
+
+// The following values must be put in wireformat, so that the MAC can be calculated.
+// RFC 2845, section 3.4.2. TSIG Variables.
+type tsigWireFmt struct {
+	// From RR_Header
+	Name  string `dns:"domain-name"`
+	Class uint16
+	Ttl   uint32
+	// Rdata of the TSIG
+	Algorithm  string `dns:"domain-name"`
+	TimeSigned uint64 `dns:"uint48"`
+	Fudge      uint16
+	// MACSize, MAC and OrigId excluded
+	Error     uint16
+	OtherLen  uint16
+	OtherData string `dns:"size-hex"`
+}
+
+// If we have the MAC use this type to convert it to wiredata.
+// Section 3.4.3. Request MAC
+type macWireFmt struct {
+	MACSize uint16
+	MAC     string `dns:"size-hex"`
+}
+
+// 3.3. Time values used in TSIG calculations
+type timerWireFmt struct {
+	TimeSigned uint64 `dns:"uint48"`
+	Fudge      uint16
+}
+
+// TsigGenerate fills out the TSIG record attached to the message.
+// The message should contain
+// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
+// time fudge (defaults to 300 seconds) and the current time
+// The TSIG MAC is saved in that Tsig RR.
+// When TsigGenerate is called for the first time requestMAC is set to the empty string and
+// timersOnly is false.
+// If something goes wrong an error is returned, otherwise it is nil.
+func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
+	if m.IsTsig() == nil {
+		panic("dns: TSIG not last RR in additional")
+	}
+	// If we barf here, the caller is to blame
+	rawsecret, err := fromBase64([]byte(secret))
+	if err != nil {
+		return nil, "", err
+	}
+
+	rr := m.Extra[len(m.Extra)-1].(*TSIG)
+	m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
+	mbuf, err := m.Pack()
+	if err != nil {
+		return nil, "", err
+	}
+	buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)
+
+	t := new(TSIG)
+	var h hash.Hash
+	switch rr.Algorithm {
+	case HmacMD5:
+		h = hmac.New(md5.New, []byte(rawsecret))
+	case HmacSHA1:
+		h = hmac.New(sha1.New, []byte(rawsecret))
+	case HmacSHA256:
+		h = hmac.New(sha256.New, []byte(rawsecret))
+	case HmacSHA512:
+		h = hmac.New(sha512.New, []byte(rawsecret))
+	default:
+		return nil, "", ErrKeyAlg
+	}
+	io.WriteString(h, string(buf))
+	t.MAC = hex.EncodeToString(h.Sum(nil))
+	t.MACSize = uint16(len(t.MAC) / 2) // Size is half!
+
+	t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
+	t.Fudge = rr.Fudge
+	t.TimeSigned = rr.TimeSigned
+	t.Algorithm = rr.Algorithm
+	t.OrigId = m.Id
+
+	tbuf := make([]byte, t.len())
+	if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
+		tbuf = tbuf[:off] // reset to actual size used
+	} else {
+		return nil, "", err
+	}
+	mbuf = append(mbuf, tbuf...)
+	rawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
+	return mbuf, t.MAC, nil
+}
+
+// TsigVerify verifies the TSIG on a message.
+// If the signature does not validate err contains the
+// error, otherwise it is nil.
+func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
+	rawsecret, err := fromBase64([]byte(secret))
+	if err != nil {
+		return err
+	}
+	// Strip the TSIG from the incoming msg
+	stripped, tsig, err := stripTsig(msg)
+	if err != nil {
+		return err
+	}
+
+	msgMAC, err := hex.DecodeString(tsig.MAC)
+	if err != nil {
+		return err
+	}
+
+	buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
+
+	// Fudge factor works both ways. A message can arrive before it was signed because
+	// of clock skew.
+	now := uint64(time.Now().Unix())
+	ti := now - tsig.TimeSigned
+	if now < tsig.TimeSigned {
+		ti = tsig.TimeSigned - now
+	}
+	if uint64(tsig.Fudge) < ti {
+		return ErrTime
+	}
+
+	var h hash.Hash
+	switch tsig.Algorithm {
+	case HmacMD5:
+		h = hmac.New(md5.New, rawsecret)
+	case HmacSHA1:
+		h = hmac.New(sha1.New, rawsecret)
+	case HmacSHA256:
+		h = hmac.New(sha256.New, rawsecret)
+	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.
+	}
+
+	if requestMAC != "" {
+		m := new(macWireFmt)
+		m.MACSize = uint16(len(requestMAC) / 2)
+		m.MAC = requestMAC
+		buf = make([]byte, len(requestMAC)) // long enough
+		n, _ := PackStruct(m, buf, 0)
+		buf = buf[:n]
+	}
+
+	tsigvar := make([]byte, DefaultMsgSize)
+	if timersOnly {
+		tsig := new(timerWireFmt)
+		tsig.TimeSigned = rr.TimeSigned
+		tsig.Fudge = rr.Fudge
+		n, _ := PackStruct(tsig, tsigvar, 0)
+		tsigvar = tsigvar[:n]
+	} else {
+		tsig := new(tsigWireFmt)
+		tsig.Name = strings.ToLower(rr.Hdr.Name)
+		tsig.Class = ClassANY
+		tsig.Ttl = rr.Hdr.Ttl
+		tsig.Algorithm = strings.ToLower(rr.Algorithm)
+		tsig.TimeSigned = rr.TimeSigned
+		tsig.Fudge = rr.Fudge
+		tsig.Error = rr.Error
+		tsig.OtherLen = rr.OtherLen
+		tsig.OtherData = rr.OtherData
+		n, _ := PackStruct(tsig, tsigvar, 0)
+		tsigvar = tsigvar[:n]
+	}
+
+	if requestMAC != "" {
+		x := append(buf, msgbuf...)
+		buf = append(x, tsigvar...)
+	} else {
+		buf = append(msgbuf, tsigvar...)
+	}
+	return buf
+}
+
+// Strip the TSIG from the raw message.
+func stripTsig(msg []byte) ([]byte, *TSIG, error) {
+	// Copied from msg.go's Unpack()
+	// Header.
+	var dh Header
+	var err error
+	dns := new(Msg)
+	rr := new(TSIG)
+	off := 0
+	tsigoff := 0
+	if off, err = UnpackStruct(&dh, msg, off); err != nil {
+		return nil, nil, err
+	}
+	if dh.Arcount == 0 {
+		return nil, nil, ErrNoSig
+	}
+	// Rcode, see msg.go Unpack()
+	if int(dh.Bits&0xF) == RcodeNotAuth {
+		return nil, nil, ErrAuth
+	}
+
+	// Arrays.
+	dns.Question = make([]Question, dh.Qdcount)
+	dns.Answer = make([]RR, dh.Ancount)
+	dns.Ns = make([]RR, dh.Nscount)
+	dns.Extra = make([]RR, dh.Arcount)
+
+	for i := 0; i < len(dns.Question); i++ {
+		off, err = UnpackStruct(&dns.Question[i], msg, off)
+		if err != nil {
+			return nil, nil, err
+		}
+	}
+	for i := 0; i < len(dns.Answer); i++ {
+		dns.Answer[i], off, err = UnpackRR(msg, off)
+		if err != nil {
+			return nil, nil, err
+		}
+	}
+	for i := 0; i < len(dns.Ns); i++ {
+		dns.Ns[i], off, err = UnpackRR(msg, off)
+		if err != nil {
+			return nil, nil, err
+		}
+	}
+	for i := 0; i < len(dns.Extra); i++ {
+		tsigoff = off
+		dns.Extra[i], off, err = UnpackRR(msg, off)
+		if err != nil {
+			return nil, nil, err
+		}
+		if dns.Extra[i].Header().Rrtype == TypeTSIG {
+			rr = dns.Extra[i].(*TSIG)
+			// Adjust Arcount.
+			arcount, _ := unpackUint16(msg, 10)
+			msg[10], msg[11] = packUint16(arcount - 1)
+			break
+		}
+	}
+	if rr == nil {
+		return nil, nil, ErrNoSig
+	}
+	return msg[:tsigoff], rr, nil
+}
+
+// Translate the TSIG time signed into a date. There is no
+// need for RFC1982 calculations as this date is 48 bits.
+func tsigTimeToString(t uint64) string {
+	ti := time.Unix(int64(t), 0).UTC()
+	return ti.Format("20060102150405")
+}

+ 1328 - 0
vendor/src/github.com/miekg/dns/types.go

@@ -0,0 +1,1328 @@
+package dns
+
+import (
+	"encoding/base64"
+	"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
+	TypeWKS        uint16 = 11
+	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
+	TypeIPSECKEY   uint16 = 45
+	TypeRRSIG      uint16 = 46
+	TypeNSEC       uint16 = 47
+	TypeDNSKEY     uint16 = 48
+	TypeDHCID      uint16 = 49
+	TypeNSEC3      uint16 = 50
+	TypeNSEC3PARAM uint16 = 51
+	TypeTLSA       uint16 = 52
+	TypeHIP        uint16 = 55
+	TypeNINFO      uint16 = 56
+	TypeRKEY       uint16 = 57
+	TypeTALINK     uint16 = 58
+	TypeCDS        uint16 = 59
+	TypeCDNSKEY    uint16 = 60
+	TypeOPENPGPKEY uint16 = 61
+	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
+
+	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.
+	RcodeSuccess        = 0
+	RcodeFormatError    = 1
+	RcodeServerFailure  = 2
+	RcodeNameError      = 3
+	RcodeNotImplemented = 4
+	RcodeRefused        = 5
+	RcodeYXDomain       = 6
+	RcodeYXRrset        = 7
+	RcodeNXRrset        = 8
+	RcodeNotAuth        = 9
+	RcodeNotZone        = 10
+	RcodeBadSig         = 16 // TSIG
+	RcodeBadVers        = 16 // EDNS0
+	RcodeBadKey         = 17
+	RcodeBadTime        = 18
+	RcodeBadMode        = 19 // TKEY
+	RcodeBadName        = 20
+	RcodeBadAlg         = 21
+	RcodeBadTrunc       = 22 // TSIG
+
+	// Message Opcodes. There is no 3.
+	OpcodeQuery  = 0
+	OpcodeIQuery = 1
+	OpcodeStatus = 2
+	OpcodeNotify = 4
+	OpcodeUpdate = 5
+)
+
+// Headers 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
+
+	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",
+}
+
+// StringToCertType is the reverseof CertTypeToString.
+var StringToCertType = reverseInt16(CertTypeToString)
+
+//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() int {
+	return len(q.Name) + 1 + 2 + 2
+}
+
+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() }
+
+type CNAME struct {
+	Hdr    RR_Header
+	Target string `dns:"cdomain-name"`
+}
+
+func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
+
+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})
+}
+
+type MB struct {
+	Hdr RR_Header
+	Mb  string `dns:"cdomain-name"`
+}
+
+func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
+
+type MG struct {
+	Hdr RR_Header
+	Mg  string `dns:"cdomain-name"`
+}
+
+func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
+
+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)
+}
+
+type MR struct {
+	Hdr RR_Header
+	Mr  string `dns:"cdomain-name"`
+}
+
+func (rr *MR) String() string {
+	return rr.Hdr.String() + sprintName(rr.Mr)
+}
+
+type MF struct {
+	Hdr RR_Header
+	Mf  string `dns:"cdomain-name"`
+}
+
+func (rr *MF) String() string {
+	return rr.Hdr.String() + sprintName(rr.Mf)
+}
+
+type MD struct {
+	Hdr RR_Header
+	Md  string `dns:"cdomain-name"`
+}
+
+func (rr *MD) String() string {
+	return rr.Hdr.String() + sprintName(rr.Md)
+}
+
+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)
+}
+
+type AFSDB struct {
+	Hdr      RR_Header
+	Subtype  uint16
+	Hostname string `dns:"cdomain-name"`
+}
+
+func (rr *AFSDB) String() string {
+	return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
+}
+
+type X25 struct {
+	Hdr         RR_Header
+	PSDNAddress string
+}
+
+func (rr *X25) String() string {
+	return rr.Hdr.String() + rr.PSDNAddress
+}
+
+type RT struct {
+	Hdr        RR_Header
+	Preference uint16
+	Host       string `dns:"cdomain-name"`
+}
+
+func (rr *RT) String() string {
+	return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
+}
+
+type NS struct {
+	Hdr RR_Header
+	Ns  string `dns:"cdomain-name"`
+}
+
+func (rr *NS) String() string {
+	return rr.Hdr.String() + sprintName(rr.Ns)
+}
+
+type PTR struct {
+	Hdr RR_Header
+	Ptr string `dns:"cdomain-name"`
+}
+
+func (rr *PTR) String() string {
+	return rr.Hdr.String() + sprintName(rr.Ptr)
+}
+
+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() + rr.Mbox + " " + sprintTxt([]string{rr.Txt})
+}
+
+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)
+}
+
+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 {
+	src := []byte(s)
+	dst := make([]byte, 0, len(src))
+	for i := 0; i < len(src); {
+		if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
+			dst = append(dst, src[i:i+2]...)
+			i += 2
+		} else {
+			b, n := nextByte(src, i)
+			if n == 0 {
+				i++ // dangling back slash
+			} else if b == '.' {
+				dst = append(dst, b)
+			} else {
+				dst = appendDomainNameByte(dst, b)
+			}
+			i += n
+		}
+	}
+	return string(dst)
+}
+
+func sprintTxtOctet(s string) string {
+	src := []byte(s)
+	dst := make([]byte, 0, len(src))
+	dst = append(dst, '"')
+	for i := 0; i < len(src); {
+		if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' {
+			dst = append(dst, src[i:i+2]...)
+			i += 2
+		} else {
+			b, n := nextByte(src, i)
+			if n == 0 {
+				i++ // dangling back slash
+			} else if b == '.' {
+				dst = append(dst, b)
+			} else {
+				if b < ' ' || b > '~' {
+					dst = appendByte(dst, b)
+				} else {
+					dst = append(dst, b)
+				}
+			}
+			i += n
+		}
+	}
+	dst = append(dst, '"')
+	return string(dst)
+}
+
+func sprintTxt(txt []string) string {
+	var out []byte
+	for i, s := range txt {
+		if i > 0 {
+			out = append(out, ` "`...)
+		} else {
+			out = append(out, '"')
+		}
+		bs := []byte(s)
+		for j := 0; j < len(bs); {
+			b, n := nextByte(bs, j)
+			if n == 0 {
+				break
+			}
+			out = appendTXTStringByte(out, b)
+			j += n
+		}
+		out = append(out, '"')
+	}
+	return string(out)
+}
+
+func appendDomainNameByte(s []byte, b byte) []byte {
+	switch b {
+	case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape
+		return append(s, '\\', b)
+	}
+	return appendTXTStringByte(s, b)
+}
+
+func appendTXTStringByte(s []byte, b byte) []byte {
+	switch b {
+	case '\t':
+		return append(s, '\\', 't')
+	case '\r':
+		return append(s, '\\', 'r')
+	case '\n':
+		return append(s, '\\', 'n')
+	case '"', '\\':
+		return append(s, '\\', b)
+	}
+	if b < ' ' || b > '~' {
+		return appendByte(s, b)
+	}
+	return append(s, b)
+}
+
+func appendByte(s []byte, b byte) []byte {
+	var buf [3]byte
+	bufs := strconv.AppendInt(buf[:0], int64(b), 10)
+	s = append(s, '\\')
+	for i := 0; i < 3-len(bufs); i++ {
+		s = append(s, '0')
+	}
+	for _, r := range bufs {
+		s = append(s, r)
+	}
+	return s
+}
+
+func nextByte(b []byte, offset int) (byte, int) {
+	if offset >= len(b) {
+		return 0, 0
+	}
+	if b[offset] != '\\' {
+		// not an escape sequence
+		return b[offset], 1
+	}
+	switch len(b) - offset {
+	case 1: // dangling escape
+		return 0, 0
+	case 2, 3: // too short to be \ddd
+	default: // maybe \ddd
+		if isDigit(b[offset+1]) && isDigit(b[offset+2]) && isDigit(b[offset+3]) {
+			return dddToByte(b[offset+1:]), 4
+		}
+	}
+	// not \ddd, maybe a control char
+	switch b[offset+1] {
+	case 't':
+		return '\t', 2
+	case 'r':
+		return '\r', 2
+	case 'n':
+		return '\n', 2
+	default:
+		return b[offset+1], 2
+	}
+}
+
+type SPF struct {
+	Hdr RR_Header
+	Txt []string `dns:"txt"`
+}
+
+func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
+
+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)
+}
+
+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
+}
+
+// The CERT resource record, 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
+}
+
+// The DNAME resource record, 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)
+}
+
+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()
+}
+
+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()
+}
+
+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)
+}
+
+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
+}
+
+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 is identical to RRSIG and nowadays only used for SIG(0), RFC2931.
+type SIG struct {
+	RRSIG
+}
+
+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
+}
+
+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() int {
+	l := rr.Hdr.len() + 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
+}
+
+type DLV struct {
+	DS
+}
+
+type CDS struct {
+	DS
+}
+
+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)
+}
+
+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)
+}
+
+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)
+}
+
+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)
+}
+
+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)
+}
+
+type IPSECKEY struct {
+	Hdr        RR_Header
+	Precedence uint8
+	// GatewayType: 1: A record, 2: AAAA record, 3: domainname.
+	// 0 is use for no type and GatewayName should be "." then.
+	GatewayType uint8
+	Algorithm   uint8
+	// Gateway can be an A record, AAAA record or a domain name.
+	GatewayA    net.IP `dns:"a"`
+	GatewayAAAA net.IP `dns:"aaaa"`
+	GatewayName string `dns:"domain-name"`
+	PublicKey   string `dns:"base64"`
+}
+
+func (rr *IPSECKEY) String() string {
+	s := rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) +
+		" " + strconv.Itoa(int(rr.GatewayType)) +
+		" " + strconv.Itoa(int(rr.Algorithm))
+	switch rr.GatewayType {
+	case 0:
+		fallthrough
+	case 3:
+		s += " " + rr.GatewayName
+	case 1:
+		s += " " + rr.GatewayA.String()
+	case 2:
+		s += " " + rr.GatewayAAAA.String()
+	default:
+		s += " ."
+	}
+	s += " " + rr.PublicKey
+	return s
+}
+
+func (rr *IPSECKEY) len() int {
+	l := rr.Hdr.len() + 3 + 1
+	switch rr.GatewayType {
+	default:
+		fallthrough
+	case 0:
+		fallthrough
+	case 3:
+		l += len(rr.GatewayName)
+	case 1:
+		l += 4
+	case 2:
+		l += 16
+	}
+	return l + base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+}
+
+type KEY struct {
+	DNSKEY
+}
+
+type CDNSKEY struct {
+	DNSKEY
+}
+
+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
+}
+
+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
+}
+
+type NSAPPTR struct {
+	Hdr RR_Header
+	Ptr string `dns:"domain-name"`
+}
+
+func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
+
+type NSEC3 struct {
+	Hdr        RR_Header
+	Hash       uint8
+	Flags      uint8
+	Iterations uint16
+	SaltLength uint8
+	Salt       string `dns:"size-hex"`
+	HashLength uint8
+	NextDomain string   `dns:"size-base32"`
+	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() int {
+	l := rr.Hdr.len() + 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
+}
+
+type NSEC3PARAM struct {
+	Hdr        RR_Header
+	Hash       uint8
+	Flags      uint8
+	Iterations uint16
+	SaltLength uint8
+	Salt       string `dns:"hex"`
+}
+
+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
+}
+
+type TKEY struct {
+	Hdr        RR_Header
+	Algorithm  string `dns:"domain-name"`
+	Inception  uint32
+	Expiration uint32
+	Mode       uint16
+	Error      uint16
+	KeySize    uint16
+	Key        string
+	OtherLen   uint16
+	OtherData  string
+}
+
+func (rr *TKEY) String() string {
+	// It has no presentation format
+	return ""
+}
+
+// RFC3597 represents an unknown/generic RR.
+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
+}
+
+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)
+}
+
+type DHCID struct {
+	Hdr    RR_Header
+	Digest string `dns:"base64"`
+}
+
+func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
+
+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
+}
+
+type HIP struct {
+	Hdr                RR_Header
+	HitLength          uint8
+	PublicKeyAlgorithm uint8
+	PublicKeyLength    uint16
+	Hit                string   `dns:"hex"`
+	PublicKey          string   `dns:"base64"`
+	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
+}
+
+type NINFO struct {
+	Hdr    RR_Header
+	ZSData []string `dns:"txt"`
+}
+
+func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
+
+type WKS struct {
+	Hdr      RR_Header
+	Address  net.IP `dns:"a"`
+	Protocol uint8
+	BitMap   []uint16 `dns:"wks"`
+}
+
+func (rr *WKS) len() int {
+	// TODO: this is missing something...
+	return rr.Hdr.len() + net.IPv4len + 1
+}
+
+func (rr *WKS) String() (s string) {
+	s = rr.Hdr.String()
+	if rr.Address != nil {
+		s += rr.Address.String()
+	}
+	// TODO(miek): missing protocol here, see /etc/protocols
+	for i := 0; i < len(rr.BitMap); i++ {
+		// should lookup the port
+		s += " " + strconv.Itoa(int(rr.BitMap[i]))
+	}
+	return s
+}
+
+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
+}
+
+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()
+}
+
+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
+}
+
+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)
+}
+
+type EUI48 struct {
+	Hdr     RR_Header
+	Address uint64 `dns:"uint48"`
+}
+
+func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
+
+type EUI64 struct {
+	Hdr     RR_Header
+	Address uint64
+}
+
+func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
+
+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)
+}
+
+type UID struct {
+	Hdr RR_Header
+	Uid uint32
+}
+
+func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
+
+type GID struct {
+	Hdr RR_Header
+	Gid uint32
+}
+
+func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
+
+type UINFO struct {
+	Hdr   RR_Header
+	Uinfo string
+}
+
+func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
+
+type EID struct {
+	Hdr      RR_Header
+	Endpoint string `dns:"hex"`
+}
+
+func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
+
+type NIMLOC struct {
+	Hdr     RR_Header
+	Locator string `dns:"hex"`
+}
+
+func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
+
+type OPENPGPKEY struct {
+	Hdr       RR_Header
+	PublicKey string `dns:"base64"`
+}
+
+func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
+
+// 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, e := time.Parse("20060102150405", s)
+	if e != nil {
+		return 0, e
+	}
+	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
+}

+ 266 - 0
vendor/src/github.com/miekg/dns/types_generate.go

@@ -0,0 +1,266 @@
+//+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":     struct{}{},
+	"NSEC3":    struct{}{},
+	"OPT":      struct{}{},
+	"WKS":      struct{}{},
+	"IPSECKEY": struct{}{},
+}
+
+var packageHdr = `
+// *** DO NOT MODIFY ***
+// AUTOGENERATED BY go generate
+
+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(`
+// Header() functions
+{{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() int {\n", name)
+		fmt.Fprintf(b, "l := rr.Hdr.len()\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"`, `dns:"domain-name"`, `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 st.Tag(i) {
+			case `dns:"-"`:
+				// ignored
+			case `dns:"cdomain-name"`, `dns:"domain-name"`:
+				o("l += len(rr.%s) + 1\n")
+			case `dns:"octet"`:
+				o("l += len(rr.%s)\n")
+			case `dns:"base64"`:
+				o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
+			case `dns:"size-hex"`, `dns:"hex"`:
+				o("l += len(rr.%s)/2 + 1\n")
+			case `dns:"a"`:
+				o("l += net.IPv4len // %s\n")
+			case `dns:"aaaa"`:
+				o("l += net.IPv6len // %s\n")
+			case `dns:"txt"`:
+				o("for _, t := range rr.%s { l += len(t) + 1 }\n")
+			case `dns:"uint48"`:
+				o("l += 6 // %s\n")
+			case "":
+				switch st.Field(i).Type().(*types.Basic).Kind() {
+				case types.Uint8:
+					o("l += 1 // %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.copyHeader()"}
+		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, "[]")
+				t = strings.TrimPrefix(t, "github.com/miekg/dns.")
+				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)
+	}
+}

+ 58 - 0
vendor/src/github.com/miekg/dns/udp.go

@@ -0,0 +1,58 @@
+// +build !windows
+
+package dns
+
+import (
+	"net"
+	"syscall"
+)
+
+// 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 }
+
+// setUDPSocketOptions sets the UDP socket options.
+// This function is implemented on a per platform basis. See udp_*.go for more details
+func setUDPSocketOptions(conn *net.UDPConn) error {
+	sa, err := getUDPSocketName(conn)
+	if err != nil {
+		return err
+	}
+	switch sa.(type) {
+	case *syscall.SockaddrInet6:
+		v6only, err := getUDPSocketOptions6Only(conn)
+		if err != nil {
+			return err
+		}
+		setUDPSocketOptions6(conn)
+		if !v6only {
+			setUDPSocketOptions4(conn)
+		}
+	case *syscall.SockaddrInet4:
+		setUDPSocketOptions4(conn)
+	}
+	return nil
+}
+
+// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
+// net.UDPAddr.
+func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
+	oob := make([]byte, 40)
+	n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
+	if err != nil {
+		return n, nil, err
+	}
+	return n, &SessionUDP{raddr, oob[:oobn]}, err
+}
+
+// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
+func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
+	n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr)
+	return n, err
+}

+ 63 - 0
vendor/src/github.com/miekg/dns/udp_linux.go

@@ -0,0 +1,63 @@
+// +build linux
+
+package dns
+
+// See:
+// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
+// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
+//
+// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
+// interface, this might not always be the correct one. This code will make sure the egress
+// packet's interface matched the ingress' one.
+
+import (
+	"net"
+	"syscall"
+)
+
+// setUDPSocketOptions4 prepares the v4 socket for sessions.
+func setUDPSocketOptions4(conn *net.UDPConn) error {
+	file, err := conn.File()
+	if err != nil {
+		return err
+	}
+	if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
+		return err
+	}
+	return nil
+}
+
+// setUDPSocketOptions6 prepares the v6 socket for sessions.
+func setUDPSocketOptions6(conn *net.UDPConn) error {
+	file, err := conn.File()
+	if err != nil {
+		return err
+	}
+	if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
+		return err
+	}
+	return nil
+}
+
+// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
+// (dualstack).
+func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
+	file, err := conn.File()
+	if err != nil {
+		return false, err
+	}
+	// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
+	v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
+	if err != nil {
+		return false, err
+	}
+	return v6only == 1, nil
+}
+
+func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
+	file, err := conn.File()
+	if err != nil {
+		return nil, err
+	}
+	return syscall.Getsockname(int(file.Fd()))
+}

+ 17 - 0
vendor/src/github.com/miekg/dns/udp_other.go

@@ -0,0 +1,17 @@
+// +build !linux
+
+package dns
+
+import (
+	"net"
+	"syscall"
+)
+
+// These do nothing. See udp_linux.go for an example of how to implement this.
+
+// We tried to adhire to some kind of naming scheme.
+
+func setUDPSocketOptions4(conn *net.UDPConn) error                 { return nil }
+func setUDPSocketOptions6(conn *net.UDPConn) error                 { return nil }
+func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error)     { return false, nil }
+func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) { return nil, nil }

+ 34 - 0
vendor/src/github.com/miekg/dns/udp_windows.go

@@ -0,0 +1,34 @@
+// +build windows
+
+package dns
+
+import "net"
+
+type SessionUDP struct {
+	raddr *net.UDPAddr
+}
+
+// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
+// net.UDPAddr.
+func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
+	n, raddr, err := conn.ReadFrom(b)
+	if err != nil {
+		return n, nil, err
+	}
+	session := &SessionUDP{raddr.(*net.UDPAddr)}
+	return n, session, err
+}
+
+// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
+func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
+	n, err := conn.WriteTo(b, session.raddr)
+	return n, err
+}
+
+func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
+
+// setUDPSocketOptions sets the UDP socket options.
+// This function is implemented on a per platform basis. See udp_*.go for more details
+func setUDPSocketOptions(conn *net.UDPConn) error {
+	return nil
+}

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

@@ -0,0 +1,94 @@
+package dns
+
+// NameUsed sets the RRs in the prereq section to
+// "Name is in use" RRs. RFC 2136 section 2.4.4.
+func (u *Msg) NameUsed(rr []RR) {
+	u.Answer = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Answer[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}
+	}
+}
+
+// NameNotUsed sets the RRs in the prereq section to
+// "Name is in not use" RRs. RFC 2136 section 2.4.5.
+func (u *Msg) NameNotUsed(rr []RR) {
+	u.Answer = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Answer[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}}
+	}
+}
+
+// Used sets the RRs in the prereq section to
+// "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2.
+func (u *Msg) Used(rr []RR) {
+	if len(u.Question) == 0 {
+		panic("dns: empty question section")
+	}
+	u.Answer = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Answer[i] = r
+		u.Answer[i].Header().Class = u.Question[0].Qclass
+	}
+}
+
+// RRsetUsed sets the RRs in the prereq section to
+// "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1.
+func (u *Msg) RRsetUsed(rr []RR) {
+	u.Answer = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Answer[i] = r
+		u.Answer[i].Header().Class = ClassANY
+		u.Answer[i].Header().Ttl = 0
+		u.Answer[i].Header().Rdlength = 0
+	}
+}
+
+// RRsetNotUsed sets the RRs in the prereq section to
+// "RRset does not exist" RRs. RFC 2136 section 2.4.3.
+func (u *Msg) RRsetNotUsed(rr []RR) {
+	u.Answer = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Answer[i] = r
+		u.Answer[i].Header().Class = ClassNONE
+		u.Answer[i].Header().Rdlength = 0
+		u.Answer[i].Header().Ttl = 0
+	}
+}
+
+// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1.
+func (u *Msg) Insert(rr []RR) {
+	if len(u.Question) == 0 {
+		panic("dns: empty question section")
+	}
+	u.Ns = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Ns[i] = r
+		u.Ns[i].Header().Class = u.Question[0].Qclass
+	}
+}
+
+// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2.
+func (u *Msg) RemoveRRset(rr []RR) {
+	u.Ns = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}}
+	}
+}
+
+// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3
+func (u *Msg) RemoveName(rr []RR) {
+	u.Ns = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}
+	}
+}
+
+// Remove creates a dynamic update packet deletes RR from the RRSset, see RFC 2136 section 2.5.4
+func (u *Msg) Remove(rr []RR) {
+	u.Ns = make([]RR, len(rr))
+	for i, r := range rr {
+		u.Ns[i] = r
+		u.Ns[i].Header().Class = ClassNONE
+		u.Ns[i].Header().Ttl = 0
+	}
+}

+ 244 - 0
vendor/src/github.com/miekg/dns/xfr.go

@@ -0,0 +1,244 @@
+package dns
+
+import (
+	"time"
+)
+
+// Envelope is used when doing a zone transfer with a remote server.
+type Envelope struct {
+	RR    []RR  // The set of RRs in the answer section of the xfr reply message.
+	Error error // If something went wrong, this contains the error.
+}
+
+// A Transfer defines parameters that are used during a zone transfer.
+type Transfer struct {
+	*Conn
+	DialTimeout    time.Duration     // net.DialTimeout, 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 fully qualified
+	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) {
+	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)
+	go func() {
+		if q.Question[0].Qtype == TypeAXFR {
+			go t.inAxfr(q.Id, env)
+			return
+		}
+		if q.Question[0].Qtype == TypeIXFR {
+			go t.inIxfr(q.Id, env)
+			return
+		}
+	}()
+	return env, nil
+}
+
+func (t *Transfer) inAxfr(id uint16, c chan *Envelope) {
+	first := true
+	defer t.Close()
+	defer close(c)
+	timeout := dnsTimeout
+	if t.ReadTimeout != 0 {
+		timeout = t.ReadTimeout
+	}
+	for {
+		t.Conn.SetReadDeadline(time.Now().Add(timeout))
+		in, err := t.ReadMsg()
+		if err != nil {
+			c <- &Envelope{nil, err}
+			return
+		}
+		if id != in.Id {
+			c <- &Envelope{in.Answer, ErrId}
+			return
+		}
+		if first {
+			if !isSOAFirst(in) {
+				c <- &Envelope{in.Answer, ErrSoa}
+				return
+			}
+			first = !first
+			// only one answer that is SOA, receive more
+			if len(in.Answer) == 1 {
+				t.tsigTimersOnly = true
+				c <- &Envelope{in.Answer, nil}
+				continue
+			}
+		}
+
+		if !first {
+			t.tsigTimersOnly = true // Subsequent envelopes use this.
+			if isSOALast(in) {
+				c <- &Envelope{in.Answer, nil}
+				return
+			}
+			c <- &Envelope{in.Answer, nil}
+		}
+	}
+}
+
+func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {
+	serial := uint32(0) // The first serial seen is the current server serial
+	first := true
+	defer t.Close()
+	defer close(c)
+	timeout := dnsTimeout
+	if t.ReadTimeout != 0 {
+		timeout = t.ReadTimeout
+	}
+	for {
+		t.SetReadDeadline(time.Now().Add(timeout))
+		in, err := t.ReadMsg()
+		if err != nil {
+			c <- &Envelope{nil, err}
+			return
+		}
+		if id != in.Id {
+			c <- &Envelope{in.Answer, ErrId}
+			return
+		}
+		if first {
+			// A single SOA RR signals "no changes"
+			if len(in.Answer) == 1 && isSOAFirst(in) {
+				c <- &Envelope{in.Answer, nil}
+				return
+			}
+
+			// Check if the returned answer is ok
+			if !isSOAFirst(in) {
+				c <- &Envelope{in.Answer, ErrSoa}
+				return
+			}
+			// This serial is important
+			serial = in.Answer[0].(*SOA).Serial
+			first = !first
+		}
+
+		// Now we need to check each message for SOA records, to see what we need to do
+		if !first {
+			t.tsigTimersOnly = true
+			// If the last record in the IXFR contains the servers' SOA,  we should quit
+			if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok {
+				if v.Serial == serial {
+					c <- &Envelope{in.Answer, nil}
+					return
+				}
+			}
+			c <- &Envelope{in.Answer, nil}
+		}
+	}
+}
+
+// Out performs an outgoing transfer with the client connecting in w.
+// Basic use pattern:
+//
+//	ch := make(chan *dns.Envelope)
+//	tr := new(dns.Transfer)
+//	tr.Out(w, r, ch)
+//	c <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
+//	close(ch)
+//	w.Hijack()
+//	// w.Close() // Client closes connection
+//
+// The server is responsible for sending the correct sequence of RRs through the
+// channel ch.
+func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error {
+	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
+	}
+	if _, err = t.Write(out); err != nil {
+		return err
+	}
+	return nil
+}
+
+func isSOAFirst(in *Msg) bool {
+	if len(in.Answer) > 0 {
+		return in.Answer[0].Header().Rrtype == TypeSOA
+	}
+	return false
+}
+
+func isSOALast(in *Msg) bool {
+	if len(in.Answer) > 0 {
+		return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA
+	}
+	return false
+}

+ 158 - 0
vendor/src/github.com/miekg/dns/zgenerate.go

@@ -0,0 +1,158 @@
+package dns
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// Parse the $GENERATE statement as used in BIND9 zones.
+// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
+// We are called after '$GENERATE '. After which we expect:
+// * the range (12-24/2)
+// * lhs (ownername)
+// * [[ttl][class]]
+// * type
+// * rhs (rdata)
+// But we are lazy here, only the range is parsed *all* occurences
+// of $ after that are interpreted.
+// Any error are returned as a string value, the empty string signals
+// "no error".
+func generate(l lex, c chan lex, t chan *Token, o string) string {
+	step := 1
+	if i := strings.IndexAny(l.token, "/"); i != -1 {
+		if i+1 == len(l.token) {
+			return "bad step in $GENERATE range"
+		}
+		if s, e := strconv.Atoi(l.token[i+1:]); e == nil {
+			if s < 0 {
+				return "bad step in $GENERATE range"
+			}
+			step = s
+		} else {
+			return "bad step in $GENERATE range"
+		}
+		l.token = l.token[:i]
+	}
+	sx := strings.SplitN(l.token, "-", 2)
+	if len(sx) != 2 {
+		return "bad start-stop in $GENERATE range"
+	}
+	start, err := strconv.Atoi(sx[0])
+	if err != nil {
+		return "bad start in $GENERATE range"
+	}
+	end, err := strconv.Atoi(sx[1])
+	if err != nil {
+		return "bad stop in $GENERATE range"
+	}
+	if end < 0 || start < 0 || end < start {
+		return "bad range in $GENERATE range"
+	}
+
+	<-c // _BLANK
+	// Create a complete new string, which we then parse again.
+	s := ""
+BuildRR:
+	l = <-c
+	if l.value != zNewline && l.value != zEOF {
+		s += l.token
+		goto BuildRR
+	}
+	for i := start; i <= end; i += step {
+		var (
+			escape bool
+			dom    bytes.Buffer
+			mod    string
+			err    string
+			offset int
+		)
+
+		for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
+			switch s[j] {
+			case '\\':
+				if escape {
+					dom.WriteByte('\\')
+					escape = false
+					continue
+				}
+				escape = true
+			case '$':
+				mod = "%d"
+				offset = 0
+				if escape {
+					dom.WriteByte('$')
+					escape = false
+					continue
+				}
+				escape = false
+				if j+1 >= len(s) { // End of the string
+					dom.WriteString(fmt.Sprintf(mod, i+offset))
+					continue
+				} else {
+					if s[j+1] == '$' {
+						dom.WriteByte('$')
+						j++
+						continue
+					}
+				}
+				// Search for { and }
+				if s[j+1] == '{' { // Modifier block
+					sep := strings.Index(s[j+2:], "}")
+					if sep == -1 {
+						return "bad modifier in $GENERATE"
+					}
+					mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
+					if err != "" {
+						return err
+					}
+					j += 2 + sep // Jump to it
+				}
+				dom.WriteString(fmt.Sprintf(mod, i+offset))
+			default:
+				if escape { // Pretty useless here
+					escape = false
+					continue
+				}
+				dom.WriteByte(s[j])
+			}
+		}
+		// Re-parse the RR and send it on the current channel t
+		rx, e := NewRR("$ORIGIN " + o + "\n" + dom.String())
+		if e != nil {
+			return e.(*ParseError).err
+		}
+		t <- &Token{RR: rx}
+		// Its more efficient to first built the rrlist and then parse it in
+		// one go! But is this a problem?
+	}
+	return ""
+}
+
+// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
+func modToPrintf(s string) (string, int, string) {
+	xs := strings.SplitN(s, ",", 3)
+	if len(xs) != 3 {
+		return "", 0, "bad modifier in $GENERATE"
+	}
+	// xs[0] is offset, xs[1] is width, xs[2] is base
+	if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
+		return "", 0, "bad base in $GENERATE"
+	}
+	offset, err := strconv.Atoi(xs[0])
+	if err != nil || offset > 255 {
+		return "", 0, "bad offset in $GENERATE"
+	}
+	width, err := strconv.Atoi(xs[1])
+	if err != nil || width > 255 {
+		return "", offset, "bad width in $GENERATE"
+	}
+	switch {
+	case width < 0:
+		return "", offset, "bad width in $GENERATE"
+	case width == 0:
+		return "%" + xs[1] + xs[2], offset, ""
+	}
+	return "%0" + xs[1] + xs[2], offset, ""
+}

+ 974 - 0
vendor/src/github.com/miekg/dns/zscan.go

@@ -0,0 +1,974 @@
+package dns
+
+import (
+	"io"
+	"log"
+	"os"
+	"strconv"
+	"strings"
+)
+
+type debugging bool
+
+const debug debugging = false
+
+func (d debugging) Printf(format string, args ...interface{}) {
+	if d {
+		log.Printf(format, args...)
+	}
+}
+
+const maxTok = 2048 // Largest token we can return.
+const maxUint16 = 1<<16 - 1
+
+// Tokinize a RFC 1035 zone file. The tokenizer will normalize it:
+// * Add ownernames if they are left blank;
+// * Suppress sequences of spaces;
+// * Make each RR fit on one line (_NEWLINE is send as last)
+// * Handle comments: ;
+// * Handle braces - anywhere.
+const (
+	// Zonefile
+	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 occured.
+type ParseError struct {
+	file string
+	err  string
+	lex  lex
+}
+
+func (e *ParseError) Error() (s string) {
+	if e.file != "" {
+		s = e.file + ": "
+	}
+	s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " +
+		strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column)
+	return
+}
+
+type lex struct {
+	token      string // text of the token
+	tokenUpper string // uppercase text of the token
+	length     int    // lenght of the token
+	err        bool   // when true, token text has lexer error
+	value      uint8  // value: zString, _BLANK, etc.
+	line       int    // line in the file
+	column     int    // column in the file
+	torc       uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar
+	comment    string // any comment text seen
+}
+
+// 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 occured, this has the error specifics.
+	Error *ParseError
+	// A potential comment positioned after the RR and on the same line.
+	Comment string
+}
+
+// NewRR reads the RR contained in the string s. Only the first RR is
+// returned. If s contains no RR, 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 q.
+// See NewRR for more documentation.
+func ReadRR(q io.Reader, filename string) (RR, error) {
+	r := <-parseZoneHelper(q, ".", filename, 1)
+	if r == nil {
+		return nil, nil
+	}
+
+	if r.Error != nil {
+		return nil, r.Error
+	}
+	return r.RR, nil
+}
+
+// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the
+// returned channel, which consist out the parsed RR, a potential comment or an error.
+// If there is an error the RR is nil. The string file is only used
+// in error reporting. The string origin is used as the initial origin, as
+// if the file would start with: $ORIGIN origin .
+// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported.
+// The channel t is closed by ParseZone when the end of r is reached.
+//
+// Basic usage pattern when reading from a string (z) containing the
+// zone data:
+//
+//	for x := range dns.ParseZone(strings.NewReader(z), "", "") {
+//		if x.Error != nil {
+//                  // 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 discarded. Comments on a line by themselves are discarded too.
+func ParseZone(r io.Reader, origin, file string) chan *Token {
+	return parseZoneHelper(r, origin, file, 10000)
+}
+
+func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan *Token {
+	t := make(chan *Token, chansize)
+	go parseZone(r, origin, file, t, 0)
+	return t
+}
+
+func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
+	defer func() {
+		if include == 0 {
+			close(t)
+		}
+	}()
+	s := scanInit(r)
+	c := make(chan lex)
+	// Start the lexer
+	go zlexer(s, c)
+	// 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.
+
+	if origin == "" {
+		origin = "."
+	}
+	origin = Fqdn(origin)
+	if _, ok := IsDomainName(origin); !ok {
+		t <- &Token{Error: &ParseError{f, "bad initial origin name", lex{}}}
+		return
+	}
+
+	st := zExpectOwnerDir // initial state
+	var h RR_Header
+	var defttl uint32 = defaultTtl
+	var prevName string
+	for l := range c {
+		// Lexer spotted an error already
+		if l.err == true {
+			t <- &Token{Error: &ParseError{f, l.token, l}}
+			return
+
+		}
+		switch st {
+		case zExpectOwnerDir:
+			// We can also expect a directive, like $TTL or $ORIGIN
+			h.Ttl = defttl
+			h.Class = ClassINET
+			switch l.value {
+			case zNewline:
+				st = zExpectOwnerDir
+			case zOwner:
+				h.Name = l.token
+				if l.token[0] == '@' {
+					h.Name = origin
+					prevName = h.Name
+					st = zExpectOwnerBl
+					break
+				}
+				if h.Name[l.length-1] != '.' {
+					h.Name = appendOrigin(h.Name, origin)
+				}
+				_, ok := IsDomainName(l.token)
+				if !ok {
+					t <- &Token{Error: &ParseError{f, "bad owner name", l}}
+					return
+				}
+				prevName = h.Name
+				st = zExpectOwnerBl
+			case zDirTtl:
+				st = zExpectDirTtlBl
+			case zDirOrigin:
+				st = zExpectDirOriginBl
+			case zDirInclude:
+				st = zExpectDirIncludeBl
+			case zDirGenerate:
+				st = zExpectDirGenerateBl
+			case zRrtpe:
+				h.Name = prevName
+				h.Rrtype = l.torc
+				st = zExpectRdata
+			case zClass:
+				h.Name = prevName
+				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 {
+					t <- &Token{Error: &ParseError{f, "not a TTL", l}}
+					return
+				}
+				h.Ttl = ttl
+				// Don't about the defttl, we should take the $TTL value
+				// defttl = ttl
+				st = zExpectAnyNoTtlBl
+
+			default:
+				t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}}
+				return
+			}
+		case zExpectDirIncludeBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}}
+				return
+			}
+			st = zExpectDirInclude
+		case zExpectDirInclude:
+			if l.value != zString {
+				t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}}
+				return
+			}
+			neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
+			l := <-c
+			switch l.value {
+			case zBlank:
+				l := <-c
+				if l.value == zString {
+					if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err {
+						t <- &Token{Error: &ParseError{f, "bad origin name", l}}
+						return
+					}
+					// a new origin is specified.
+					if l.token[l.length-1] != '.' {
+						if origin != "." { // Prevent .. endings
+							neworigin = l.token + "." + origin
+						} else {
+							neworigin = l.token + origin
+						}
+					} else {
+						neworigin = l.token
+					}
+				}
+			case zNewline, zEOF:
+				// Ok
+			default:
+				t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}}
+				return
+			}
+			// Start with the new file
+			r1, e1 := os.Open(l.token)
+			if e1 != nil {
+				t <- &Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}}
+				return
+			}
+			if include+1 > 7 {
+				t <- &Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}}
+				return
+			}
+			parseZone(r1, l.token, neworigin, t, include+1)
+			st = zExpectOwnerDir
+		case zExpectDirTtlBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}}
+				return
+			}
+			st = zExpectDirTtl
+		case zExpectDirTtl:
+			if l.value != zString {
+				t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
+				return
+			}
+			if e, _ := slurpRemainder(c, f); e != nil {
+				t <- &Token{Error: e}
+				return
+			}
+			ttl, ok := stringToTtl(l.token)
+			if !ok {
+				t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}}
+				return
+			}
+			defttl = ttl
+			st = zExpectOwnerDir
+		case zExpectDirOriginBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}}
+				return
+			}
+			st = zExpectDirOrigin
+		case zExpectDirOrigin:
+			if l.value != zString {
+				t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}}
+				return
+			}
+			if e, _ := slurpRemainder(c, f); e != nil {
+				t <- &Token{Error: e}
+			}
+			if _, ok := IsDomainName(l.token); !ok {
+				t <- &Token{Error: &ParseError{f, "bad origin name", l}}
+				return
+			}
+			if l.token[l.length-1] != '.' {
+				if origin != "." { // Prevent .. endings
+					origin = l.token + "." + origin
+				} else {
+					origin = l.token + origin
+				}
+			} else {
+				origin = l.token
+			}
+			st = zExpectOwnerDir
+		case zExpectDirGenerateBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}}
+				return
+			}
+			st = zExpectDirGenerate
+		case zExpectDirGenerate:
+			if l.value != zString {
+				t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}}
+				return
+			}
+			if e := generate(l, c, t, origin); e != "" {
+				t <- &Token{Error: &ParseError{f, e, l}}
+				return
+			}
+			st = zExpectOwnerDir
+		case zExpectOwnerBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank after owner", l}}
+				return
+			}
+			st = zExpectAny
+		case zExpectAny:
+			switch l.value {
+			case zRrtpe:
+				h.Rrtype = l.torc
+				st = zExpectRdata
+			case zClass:
+				h.Class = l.torc
+				st = zExpectAnyNoClassBl
+			case zString:
+				ttl, ok := stringToTtl(l.token)
+				if !ok {
+					t <- &Token{Error: &ParseError{f, "not a TTL", l}}
+					return
+				}
+				h.Ttl = ttl
+				// defttl = ttl // don't set the defttl here
+				st = zExpectAnyNoTtlBl
+			default:
+				t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}}
+				return
+			}
+		case zExpectAnyNoClassBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank before class", l}}
+				return
+			}
+			st = zExpectAnyNoClass
+		case zExpectAnyNoTtlBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank before TTL", l}}
+				return
+			}
+			st = zExpectAnyNoTtl
+		case zExpectAnyNoTtl:
+			switch l.value {
+			case zClass:
+				h.Class = l.torc
+				st = zExpectRrtypeBl
+			case zRrtpe:
+				h.Rrtype = l.torc
+				st = zExpectRdata
+			default:
+				t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}}
+				return
+			}
+		case zExpectAnyNoClass:
+			switch l.value {
+			case zString:
+				ttl, ok := stringToTtl(l.token)
+				if !ok {
+					t <- &Token{Error: &ParseError{f, "not a TTL", l}}
+					return
+				}
+				h.Ttl = ttl
+				// defttl = ttl // don't set the def ttl anymore
+				st = zExpectRrtypeBl
+			case zRrtpe:
+				h.Rrtype = l.torc
+				st = zExpectRdata
+			default:
+				t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}}
+				return
+			}
+		case zExpectRrtypeBl:
+			if l.value != zBlank {
+				t <- &Token{Error: &ParseError{f, "no blank before RR type", l}}
+				return
+			}
+			st = zExpectRrtype
+		case zExpectRrtype:
+			if l.value != zRrtpe {
+				t <- &Token{Error: &ParseError{f, "unknown RR type", l}}
+				return
+			}
+			h.Rrtype = l.torc
+			st = zExpectRdata
+		case zExpectRdata:
+			r, e, c1 := setRR(h, c, origin, f)
+			if e != nil {
+				// If e.lex is nil than we have encounter a unknown RR type
+				// in that case we substitute our current lex token
+				if e.lex.token == "" && e.lex.value == 0 {
+					e.lex = l // Uh, dirty
+				}
+				t <- &Token{Error: e}
+				return
+			}
+			t <- &Token{RR: r, Comment: c1}
+			st = zExpectOwnerDir
+		}
+	}
+	// If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this
+	// is not an error, because an empty zone file is still a zone file.
+}
+
+// zlexer scans the sourcefile and returns tokens on the channel c.
+func zlexer(s *scan, c chan lex) {
+	var l lex
+	str := make([]byte, maxTok) // Should be enough for any token
+	stri := 0                   // Offset in str (0 means empty)
+	com := make([]byte, maxTok) // Hold comment text
+	comi := 0
+	quote := false
+	escape := false
+	space := false
+	commt := false
+	rrtype := false
+	owner := true
+	brace := 0
+	x, err := s.tokenText()
+	defer close(c)
+	for err == nil {
+		l.column = s.position.Column
+		l.line = s.position.Line
+		if stri >= maxTok {
+			l.token = "token length insufficient for parsing"
+			l.err = true
+			debug.Printf("[%+v]", l.token)
+			c <- l
+			return
+		}
+		if comi >= maxTok {
+			l.token = "comment length insufficient for parsing"
+			l.err = true
+			debug.Printf("[%+v]", l.token)
+			c <- l
+			return
+		}
+
+		switch x {
+		case ' ', '\t':
+			if escape {
+				escape = false
+				str[stri] = x
+				stri++
+				break
+			}
+			if quote {
+				// Inside quotes this is legal
+				str[stri] = x
+				stri++
+				break
+			}
+			if commt {
+				com[comi] = x
+				comi++
+				break
+			}
+			if stri == 0 {
+				// Space directly in the beginning, handled in the grammar
+			} else if owner {
+				// If we have a string and its the first, make it an owner
+				l.value = zOwner
+				l.token = string(str[:stri])
+				l.tokenUpper = strings.ToUpper(l.token)
+				l.length = stri
+				// escape $... start with a \ not a $, so this will work
+				switch l.tokenUpper {
+				case "$TTL":
+					l.value = zDirTtl
+				case "$ORIGIN":
+					l.value = zDirOrigin
+				case "$INCLUDE":
+					l.value = zDirInclude
+				case "$GENERATE":
+					l.value = zDirGenerate
+				}
+				debug.Printf("[7 %+v]", l.token)
+				c <- l
+			} else {
+				l.value = zString
+				l.token = string(str[:stri])
+				l.tokenUpper = strings.ToUpper(l.token)
+				l.length = stri
+				if !rrtype {
+					if t, ok := StringToType[l.tokenUpper]; ok {
+						l.value = zRrtpe
+						l.torc = t
+						rrtype = true
+					} else {
+						if strings.HasPrefix(l.tokenUpper, "TYPE") {
+							t, ok := typeToInt(l.token)
+							if !ok {
+								l.token = "unknown RR type"
+								l.err = true
+								c <- l
+								return
+							}
+							l.value = zRrtpe
+							l.torc = t
+						}
+					}
+					if t, ok := StringToClass[l.tokenUpper]; ok {
+						l.value = zClass
+						l.torc = t
+					} else {
+						if strings.HasPrefix(l.tokenUpper, "CLASS") {
+							t, ok := classToInt(l.token)
+							if !ok {
+								l.token = "unknown class"
+								l.err = true
+								c <- l
+								return
+							}
+							l.value = zClass
+							l.torc = t
+						}
+					}
+				}
+				debug.Printf("[6 %+v]", l.token)
+				c <- l
+			}
+			stri = 0
+			// I reverse space stuff here
+			if !space && !commt {
+				l.value = zBlank
+				l.token = " "
+				l.length = 1
+				debug.Printf("[5 %+v]", l.token)
+				c <- l
+			}
+			owner = false
+			space = true
+		case ';':
+			if escape {
+				escape = false
+				str[stri] = x
+				stri++
+				break
+			}
+			if quote {
+				// Inside quotes this is legal
+				str[stri] = x
+				stri++
+				break
+			}
+			if stri > 0 {
+				l.value = zString
+				l.token = string(str[:stri])
+				l.length = stri
+				debug.Printf("[4 %+v]", l.token)
+				c <- l
+				stri = 0
+			}
+			commt = true
+			com[comi] = ';'
+			comi++
+		case '\r':
+			escape = false
+			if quote {
+				str[stri] = x
+				stri++
+				break
+			}
+			// discard if outside of quotes
+		case '\n':
+			escape = false
+			// Escaped newline
+			if quote {
+				str[stri] = x
+				stri++
+				break
+			}
+			// inside quotes this is legal
+			if commt {
+				// Reset a comment
+				commt = false
+				rrtype = false
+				stri = 0
+				// If not in a brace this ends the comment AND the RR
+				if brace == 0 {
+					owner = true
+					owner = true
+					l.value = zNewline
+					l.token = "\n"
+					l.length = 1
+					l.comment = string(com[:comi])
+					debug.Printf("[3 %+v %+v]", l.token, l.comment)
+					c <- l
+					l.comment = ""
+					comi = 0
+					break
+				}
+				com[comi] = ' ' // convert newline to space
+				comi++
+				break
+			}
+
+			if brace == 0 {
+				// If there is previous text, we should output it here
+				if stri != 0 {
+					l.value = zString
+					l.token = string(str[:stri])
+					l.tokenUpper = strings.ToUpper(l.token)
+
+					l.length = stri
+					if !rrtype {
+						if t, ok := StringToType[l.tokenUpper]; ok {
+							l.value = zRrtpe
+							l.torc = t
+							rrtype = true
+						}
+					}
+					debug.Printf("[2 %+v]", l.token)
+					c <- l
+				}
+				l.value = zNewline
+				l.token = "\n"
+				l.length = 1
+				debug.Printf("[1 %+v]", l.token)
+				c <- l
+				stri = 0
+				commt = false
+				rrtype = false
+				owner = true
+				comi = 0
+			}
+		case '\\':
+			// comments do not get escaped chars, everything is copied
+			if commt {
+				com[comi] = x
+				comi++
+				break
+			}
+			// something already escaped must be in string
+			if escape {
+				str[stri] = x
+				stri++
+				escape = false
+				break
+			}
+			// something escaped outside of string gets added to string
+			str[stri] = x
+			stri++
+			escape = true
+		case '"':
+			if commt {
+				com[comi] = x
+				comi++
+				break
+			}
+			if escape {
+				str[stri] = x
+				stri++
+				escape = false
+				break
+			}
+			space = false
+			// send previous gathered text and the quote
+			if stri != 0 {
+				l.value = zString
+				l.token = string(str[:stri])
+				l.length = stri
+
+				debug.Printf("[%+v]", l.token)
+				c <- l
+				stri = 0
+			}
+
+			// send quote itself as separate token
+			l.value = zQuote
+			l.token = "\""
+			l.length = 1
+			c <- l
+			quote = !quote
+		case '(', ')':
+			if commt {
+				com[comi] = x
+				comi++
+				break
+			}
+			if escape {
+				str[stri] = x
+				stri++
+				escape = false
+				break
+			}
+			if quote {
+				str[stri] = x
+				stri++
+				break
+			}
+			switch x {
+			case ')':
+				brace--
+				if brace < 0 {
+					l.token = "extra closing brace"
+					l.err = true
+					debug.Printf("[%+v]", l.token)
+					c <- l
+					return
+				}
+			case '(':
+				brace++
+			}
+		default:
+			escape = false
+			if commt {
+				com[comi] = x
+				comi++
+				break
+			}
+			str[stri] = x
+			stri++
+			space = false
+		}
+		x, err = s.tokenText()
+	}
+	if stri > 0 {
+		// Send remainder
+		l.token = string(str[:stri])
+		l.length = stri
+		l.value = zString
+		debug.Printf("[%+v]", l.token)
+		c <- l
+	}
+}
+
+// Extract the class number from CLASSxx
+func classToInt(token string) (uint16, bool) {
+	offset := 5
+	if len(token) < offset+1 {
+		return 0, false
+	}
+	class, ok := strconv.Atoi(token[offset:])
+	if ok != nil || class > maxUint16 {
+		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, ok := strconv.Atoi(token[offset:])
+	if ok != nil || typ > maxUint16 {
+		return 0, false
+	}
+	return uint16(typ), true
+}
+
+// Parse things like 2w, 2m, etc, Return the time in seconds.
+func stringToTtl(token string) (uint32, bool) {
+	s := uint32(0)
+	i := uint32(0)
+	for _, c := range token {
+		switch c {
+		case 's', 'S':
+			s += i
+			i = 0
+		case 'm', 'M':
+			s += i * 60
+			i = 0
+		case 'h', 'H':
+			s += i * 60 * 60
+			i = 0
+		case 'd', 'D':
+			s += i * 60 * 60 * 24
+			i = 0
+		case 'w', 'W':
+			s += i * 60 * 60 * 24 * 7
+			i = 0
+		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+			i *= 10
+			i += uint32(c) - '0'
+		default:
+			return 0, false
+		}
+	}
+	return s + i, true
+}
+
+// Parse LOC records' <digits>[.<digits>][mM] into a
+// mantissa exponent format. Token should contain the entire
+// string (i.e. no spaces allowed)
+func stringToCm(token string) (e, m uint8, ok bool) {
+	if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' {
+		token = token[0 : len(token)-1]
+	}
+	s := strings.SplitN(token, ".", 2)
+	var meters, cmeters, val int
+	var err error
+	switch len(s) {
+	case 2:
+		if cmeters, err = strconv.Atoi(s[1]); err != nil {
+			return
+		}
+		fallthrough
+	case 1:
+		if meters, err = strconv.Atoi(s[0]); err != nil {
+			return
+		}
+	case 0:
+		// huh?
+		return 0, 0, false
+	}
+	ok = true
+	if meters > 0 {
+		e = 2
+		val = meters
+	} else {
+		e = 0
+		val = cmeters
+	}
+	for val > 10 {
+		e++
+		val /= 10
+	}
+	if e > 9 {
+		ok = false
+	}
+	m = uint8(val)
+	return
+}
+
+func appendOrigin(name, origin string) string {
+	if origin == "." {
+		return name + origin
+	}
+	return name + "." + origin
+}
+
+// LOC record helper function
+func locCheckNorth(token string, latitude uint32) (uint32, bool) {
+	switch token {
+	case "n", "N":
+		return LOC_EQUATOR + latitude, true
+	case "s", "S":
+		return LOC_EQUATOR - latitude, true
+	}
+	return latitude, false
+}
+
+// LOC record helper function
+func locCheckEast(token string, longitude uint32) (uint32, bool) {
+	switch token {
+	case "e", "E":
+		return LOC_EQUATOR + longitude, true
+	case "w", "W":
+		return LOC_EQUATOR - longitude, true
+	}
+	return longitude, false
+}
+
+// "Eat" the rest of the "line". Return potential comments
+func slurpRemainder(c chan lex, f string) (*ParseError, string) {
+	l := <-c
+	com := ""
+	switch l.value {
+	case zBlank:
+		l = <-c
+		com = l.comment
+		if l.value != zNewline && l.value != zEOF {
+			return &ParseError{f, "garbage after rdata", l}, ""
+		}
+	case zNewline:
+		com = l.comment
+	case zEOF:
+	default:
+		return &ParseError{f, "garbage after rdata", l}, ""
+	}
+	return nil, com
+}
+
+// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64"
+// Used for NID and L64 record.
+func stringToNodeID(l lex) (uint64, *ParseError) {
+	if len(l.token) < 19 {
+		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
+	}
+	// There must be three colons at fixes postitions, if not its a parse error
+	if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
+		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
+	}
+	s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19]
+	u, e := strconv.ParseUint(s, 16, 64)
+	if e != nil {
+		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
+	}
+	return u, nil
+}

+ 2270 - 0
vendor/src/github.com/miekg/dns/zscan_rr.go

@@ -0,0 +1,2270 @@
+package dns
+
+import (
+	"encoding/base64"
+	"net"
+	"strconv"
+	"strings"
+)
+
+type parserFunc struct {
+	// Func defines the function that parses the tokens and returns the RR
+	// or an error. The last string contains any comments in the line as
+	// they returned by the lexer as well.
+	Func func(h RR_Header, c chan lex, origin string, file string) (RR, *ParseError, string)
+	// Signals if the RR ending is of variable length, like TXT or records
+	// that have Hexadecimal or Base64 as their last element in the Rdata. Records
+	// that have a fixed ending or for instance A, AAAA, SOA and etc.
+	Variable bool
+}
+
+// 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 chan lex, o, f string) (RR, *ParseError, string) {
+	parserfunc, ok := typeToparserFunc[h.Rrtype]
+	if ok {
+		r, e, cm := parserfunc.Func(h, c, o, f)
+		if parserfunc.Variable {
+			return r, e, cm
+		}
+		if e != nil {
+			return nil, e, ""
+		}
+		e, cm = slurpRemainder(c, f)
+		if e != nil {
+			return nil, e, ""
+		}
+		return r, nil, cm
+	}
+	// RFC3957 RR (Unknown RR handling)
+	return setRFC3597(h, c, o, f)
+}
+
+// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces)
+// or an error
+func endingToString(c chan lex, errstr, f string) (string, *ParseError, string) {
+	s := ""
+	l := <-c // 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
+	}
+	return s, nil, l.comment
+}
+
+// A remainder of the rdata with embedded spaces, return the parsed string slice (sans the spaces)
+// or an error
+func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, string) {
+	// Get the remaining data until we see a zNewline
+	quote := false
+	l := <-c
+	var s []string
+	if l.err {
+		return s, &ParseError{f, errstr, l}, ""
+	}
+	switch l.value == zQuote {
+	case true: // A number of quoted string
+		s = make([]string, 0)
+		empty := true
+		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
+		}
+		if quote {
+			return nil, &ParseError{f, errstr, l}, ""
+		}
+	case false: // Unquoted text record
+		s = make([]string, 1)
+		for l.value != zNewline && l.value != zEOF {
+			if l.err {
+				return s, &ParseError{f, errstr, l}, ""
+			}
+			s[0] += l.token
+			l = <-c
+		}
+	}
+	return s, nil, l.comment
+}
+
+func setA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(A)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 { // Dynamic updates.
+		return rr, nil, ""
+	}
+	rr.A = net.ParseIP(l.token)
+	if rr.A == nil || l.err {
+		return nil, &ParseError{f, "bad A A", l}, ""
+	}
+	return rr, nil, ""
+}
+
+func setAAAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(AAAA)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	rr.AAAA = net.ParseIP(l.token)
+	if rr.AAAA == nil || l.err {
+		return nil, &ParseError{f, "bad AAAA AAAA", l}, ""
+	}
+	return rr, nil, ""
+}
+
+func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NS)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Ns = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Ns = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad NS Ns", l}, ""
+	}
+	if rr.Ns[l.length-1] != '.' {
+		rr.Ns = appendOrigin(rr.Ns, o)
+	}
+	return rr, nil, ""
+}
+
+func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(PTR)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Ptr = l.token
+	if l.length == 0 { // dynamic update rr.
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Ptr = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad PTR Ptr", l}, ""
+	}
+	if rr.Ptr[l.length-1] != '.' {
+		rr.Ptr = appendOrigin(rr.Ptr, o)
+	}
+	return rr, nil, ""
+}
+
+func setNSAPPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NSAPPTR)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Ptr = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Ptr = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad NSAP-PTR Ptr", l}, ""
+	}
+	if rr.Ptr[l.length-1] != '.' {
+		rr.Ptr = appendOrigin(rr.Ptr, o)
+	}
+	return rr, nil, ""
+}
+
+func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(RP)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Mbox = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Mbox = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad RP Mbox", l}, ""
+		}
+		if rr.Mbox[l.length-1] != '.' {
+			rr.Mbox = appendOrigin(rr.Mbox, o)
+		}
+	}
+	<-c // zBlank
+	l = <-c
+	rr.Txt = l.token
+	if l.token == "@" {
+		rr.Txt = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad RP Txt", l}, ""
+	}
+	if rr.Txt[l.length-1] != '.' {
+		rr.Txt = appendOrigin(rr.Txt, o)
+	}
+	return rr, nil, ""
+}
+
+func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MR)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Mr = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Mr = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MR Mr", l}, ""
+	}
+	if rr.Mr[l.length-1] != '.' {
+		rr.Mr = appendOrigin(rr.Mr, o)
+	}
+	return rr, nil, ""
+}
+
+func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MB)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Mb = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Mb = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MB Mb", l}, ""
+	}
+	if rr.Mb[l.length-1] != '.' {
+		rr.Mb = appendOrigin(rr.Mb, o)
+	}
+	return rr, nil, ""
+}
+
+func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MG)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Mg = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Mg = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MG Mg", l}, ""
+	}
+	if rr.Mg[l.length-1] != '.' {
+		rr.Mg = appendOrigin(rr.Mg, o)
+	}
+	return rr, nil, ""
+}
+
+func setHINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(HINFO)
+	rr.Hdr = h
+
+	chunks, e, c1 := endingToTxtSlice(c, "bad HINFO Fields", f)
+	if e != nil {
+		return nil, e, c1
+	}
+
+	if ln := len(chunks); ln == 0 {
+		return rr, 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 rr, nil, ""
+}
+
+func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MINFO)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Rmail = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Rmail = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad MINFO Rmail", l}, ""
+		}
+		if rr.Rmail[l.length-1] != '.' {
+			rr.Rmail = appendOrigin(rr.Rmail, o)
+		}
+	}
+	<-c // zBlank
+	l = <-c
+	rr.Email = l.token
+	if l.token == "@" {
+		rr.Email = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MINFO Email", l}, ""
+	}
+	if rr.Email[l.length-1] != '.' {
+		rr.Email = appendOrigin(rr.Email, o)
+	}
+	return rr, nil, ""
+}
+
+func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MF)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Mf = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Mf = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MF Mf", l}, ""
+	}
+	if rr.Mf[l.length-1] != '.' {
+		rr.Mf = appendOrigin(rr.Mf, o)
+	}
+	return rr, nil, ""
+}
+
+func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MD)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Md = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Md = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MD Md", l}, ""
+	}
+	if rr.Md[l.length-1] != '.' {
+		rr.Md = appendOrigin(rr.Md, o)
+	}
+	return rr, nil, ""
+}
+
+func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(MX)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad MX Pref", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Mx = l.token
+	if l.token == "@" {
+		rr.Mx = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad MX Mx", l}, ""
+	}
+	if rr.Mx[l.length-1] != '.' {
+		rr.Mx = appendOrigin(rr.Mx, o)
+	}
+	return rr, nil, ""
+}
+
+func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(RT)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil {
+		return nil, &ParseError{f, "bad RT Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Host = l.token
+	if l.token == "@" {
+		rr.Host = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad RT Host", l}, ""
+	}
+	if rr.Host[l.length-1] != '.' {
+		rr.Host = appendOrigin(rr.Host, o)
+	}
+	return rr, nil, ""
+}
+
+func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(AFSDB)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad AFSDB Subtype", l}, ""
+	}
+	rr.Subtype = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Hostname = l.token
+	if l.token == "@" {
+		rr.Hostname = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad AFSDB Hostname", l}, ""
+	}
+	if rr.Hostname[l.length-1] != '.' {
+		rr.Hostname = appendOrigin(rr.Hostname, o)
+	}
+	return rr, nil, ""
+}
+
+func setX25(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(X25)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.err {
+		return nil, &ParseError{f, "bad X25 PSDNAddress", l}, ""
+	}
+	rr.PSDNAddress = l.token
+	return rr, nil, ""
+}
+
+func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(KX)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad KX Pref", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Exchanger = l.token
+	if l.token == "@" {
+		rr.Exchanger = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad KX Exchanger", l}, ""
+	}
+	if rr.Exchanger[l.length-1] != '.' {
+		rr.Exchanger = appendOrigin(rr.Exchanger, o)
+	}
+	return rr, nil, ""
+}
+
+func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(CNAME)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Target = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Target = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad CNAME Target", l}, ""
+	}
+	if rr.Target[l.length-1] != '.' {
+		rr.Target = appendOrigin(rr.Target, o)
+	}
+	return rr, nil, ""
+}
+
+func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(DNAME)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Target = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Target = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad CNAME Target", l}, ""
+	}
+	if rr.Target[l.length-1] != '.' {
+		rr.Target = appendOrigin(rr.Target, o)
+	}
+	return rr, nil, ""
+}
+
+func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(SOA)
+	rr.Hdr = h
+
+	l := <-c
+	rr.Ns = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	<-c // zBlank
+	if l.token == "@" {
+		rr.Ns = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad SOA Ns", l}, ""
+		}
+		if rr.Ns[l.length-1] != '.' {
+			rr.Ns = appendOrigin(rr.Ns, o)
+		}
+	}
+
+	l = <-c
+	rr.Mbox = l.token
+	if l.token == "@" {
+		rr.Mbox = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad SOA Mbox", l}, ""
+		}
+		if rr.Mbox[l.length-1] != '.' {
+			rr.Mbox = appendOrigin(rr.Mbox, o)
+		}
+	}
+	<-c // zBlank
+
+	var (
+		v  uint32
+		ok bool
+	)
+	for i := 0; i < 5; i++ {
+		l = <-c
+		if l.err {
+			return nil, &ParseError{f, "bad SOA zone parameter", l}, ""
+		}
+		if j, e := strconv.Atoi(l.token); e != nil {
+			if i == 0 {
+				// Serial should be a number
+				return nil, &ParseError{f, "bad SOA zone parameter", l}, ""
+			}
+			if v, ok = stringToTtl(l.token); !ok {
+				return nil, &ParseError{f, "bad SOA zone parameter", l}, ""
+
+			}
+		} else {
+			v = uint32(j)
+		}
+		switch i {
+		case 0:
+			rr.Serial = v
+			<-c // zBlank
+		case 1:
+			rr.Refresh = v
+			<-c // zBlank
+		case 2:
+			rr.Retry = v
+			<-c // zBlank
+		case 3:
+			rr.Expire = v
+			<-c // zBlank
+		case 4:
+			rr.Minttl = v
+		}
+	}
+	return rr, nil, ""
+}
+
+func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(SRV)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad SRV Priority", l}, ""
+	}
+	rr.Priority = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad SRV Weight", l}, ""
+	}
+	rr.Weight = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad SRV Port", l}, ""
+	}
+	rr.Port = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Target = l.token
+	if l.token == "@" {
+		rr.Target = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad SRV Target", l}, ""
+	}
+	if rr.Target[l.length-1] != '.' {
+		rr.Target = appendOrigin(rr.Target, o)
+	}
+	return rr, nil, ""
+}
+
+func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NAPTR)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NAPTR Order", l}, ""
+	}
+	rr.Order = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NAPTR Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	// Flags
+	<-c     // zBlank
+	l = <-c // _QUOTE
+	if l.value != zQuote {
+		return nil, &ParseError{f, "bad NAPTR Flags", l}, ""
+	}
+	l = <-c // Either String or Quote
+	if l.value == zString {
+		rr.Flags = l.token
+		l = <-c // _QUOTE
+		if l.value != zQuote {
+			return nil, &ParseError{f, "bad NAPTR Flags", l}, ""
+		}
+	} else if l.value == zQuote {
+		rr.Flags = ""
+	} else {
+		return nil, &ParseError{f, "bad NAPTR Flags", l}, ""
+	}
+
+	// Service
+	<-c     // zBlank
+	l = <-c // _QUOTE
+	if l.value != zQuote {
+		return nil, &ParseError{f, "bad NAPTR Service", l}, ""
+	}
+	l = <-c // Either String or Quote
+	if l.value == zString {
+		rr.Service = l.token
+		l = <-c // _QUOTE
+		if l.value != zQuote {
+			return nil, &ParseError{f, "bad NAPTR Service", l}, ""
+		}
+	} else if l.value == zQuote {
+		rr.Service = ""
+	} else {
+		return nil, &ParseError{f, "bad NAPTR Service", l}, ""
+	}
+
+	// Regexp
+	<-c     // zBlank
+	l = <-c // _QUOTE
+	if l.value != zQuote {
+		return nil, &ParseError{f, "bad NAPTR Regexp", l}, ""
+	}
+	l = <-c // Either String or Quote
+	if l.value == zString {
+		rr.Regexp = l.token
+		l = <-c // _QUOTE
+		if l.value != zQuote {
+			return nil, &ParseError{f, "bad NAPTR Regexp", l}, ""
+		}
+	} else if l.value == zQuote {
+		rr.Regexp = ""
+	} else {
+		return nil, &ParseError{f, "bad NAPTR Regexp", l}, ""
+	}
+	// After quote no space??
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Replacement = l.token
+	if l.token == "@" {
+		rr.Replacement = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad NAPTR Replacement", l}, ""
+	}
+	if rr.Replacement[l.length-1] != '.' {
+		rr.Replacement = appendOrigin(rr.Replacement, o)
+	}
+	return rr, nil, ""
+}
+
+func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(TALINK)
+	rr.Hdr = h
+
+	l := <-c
+	rr.PreviousName = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.PreviousName = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad TALINK PreviousName", l}, ""
+		}
+		if rr.PreviousName[l.length-1] != '.' {
+			rr.PreviousName = appendOrigin(rr.PreviousName, o)
+		}
+	}
+	<-c // zBlank
+	l = <-c
+	rr.NextName = l.token
+	if l.token == "@" {
+		rr.NextName = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad TALINK NextName", l}, ""
+	}
+	if rr.NextName[l.length-1] != '.' {
+		rr.NextName = appendOrigin(rr.NextName, o)
+	}
+	return rr, nil, ""
+}
+
+func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(LOC)
+	rr.Hdr = h
+	// 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
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad LOC Latitude", l}, ""
+	}
+	rr.Latitude = 1000 * 60 * 60 * uint32(i)
+
+	<-c // zBlank
+	// Either number, 'N' or 'S'
+	l = <-c
+	if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
+		goto East
+	}
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad LOC Latitude minutes", l}, ""
+	}
+	rr.Latitude += 1000 * 60 * uint32(i)
+
+	<-c // zBlank
+	l = <-c
+	if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
+		return nil, &ParseError{f, "bad LOC Latitude seconds", l}, ""
+	} else {
+		rr.Latitude += uint32(1000 * i)
+	}
+	<-c // zBlank
+	// Either number, 'N' or 'S'
+	l = <-c
+	if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
+		goto East
+	}
+	// If still alive, flag an error
+	return nil, &ParseError{f, "bad LOC Latitude North/South", l}, ""
+
+East:
+	// East
+	<-c // zBlank
+	l = <-c
+	if i, e := strconv.Atoi(l.token); e != nil || l.err {
+		return nil, &ParseError{f, "bad LOC Longitude", l}, ""
+	} else {
+		rr.Longitude = 1000 * 60 * 60 * uint32(i)
+	}
+	<-c // zBlank
+	// Either number, 'E' or 'W'
+	l = <-c
+	if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
+		goto Altitude
+	}
+	if i, e := strconv.Atoi(l.token); e != nil || l.err {
+		return nil, &ParseError{f, "bad LOC Longitude minutes", l}, ""
+	} else {
+		rr.Longitude += 1000 * 60 * uint32(i)
+	}
+	<-c // zBlank
+	l = <-c
+	if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
+		return nil, &ParseError{f, "bad LOC Longitude seconds", l}, ""
+	} else {
+		rr.Longitude += uint32(1000 * i)
+	}
+	<-c // zBlank
+	// Either number, 'E' or 'W'
+	l = <-c
+	if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok {
+		goto Altitude
+	}
+	// If still alive, flag an error
+	return nil, &ParseError{f, "bad LOC Longitude East/West", l}, ""
+
+Altitude:
+	<-c // zBlank
+	l = <-c
+	if l.length == 0 || l.err {
+		return nil, &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 nil, &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
+	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 nil, &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 nil, &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 nil, &ParseError{f, "bad LOC VertPre", l}, ""
+				}
+				rr.VertPre = (e & 0x0f) | (m << 4 & 0xf0)
+			}
+			count++
+		case zBlank:
+			// Ok
+		default:
+			return nil, &ParseError{f, "bad LOC Size, HorizPre or VertPre", l}, ""
+		}
+		l = <-c
+	}
+	return rr, nil, ""
+}
+
+func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(HIP)
+	rr.Hdr = h
+
+	// HitLength is not represented
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad HIP PublicKeyAlgorithm", l}, ""
+	}
+	rr.PublicKeyAlgorithm = uint8(i)
+	<-c     // zBlank
+	l = <-c // zString
+	if l.length == 0 || l.err {
+		return nil, &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     // zBlank
+	l = <-c // zString
+	if l.length == 0 || l.err {
+		return nil, &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
+	var xs []string
+	for l.value != zNewline && l.value != zEOF {
+		switch l.value {
+		case zString:
+			if l.token == "@" {
+				xs = append(xs, o)
+				l = <-c
+				continue
+			}
+			_, ok := IsDomainName(l.token)
+			if !ok || l.length == 0 || l.err {
+				return nil, &ParseError{f, "bad HIP RendezvousServers", l}, ""
+			}
+			if l.token[l.length-1] != '.' {
+				l.token = appendOrigin(l.token, o)
+			}
+			xs = append(xs, l.token)
+		case zBlank:
+			// Ok
+		default:
+			return nil, &ParseError{f, "bad HIP RendezvousServers", l}, ""
+		}
+		l = <-c
+	}
+	rr.RendezvousServers = xs
+	return rr, nil, l.comment
+}
+
+func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(CERT)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	if v, ok := StringToCertType[l.token]; ok {
+		rr.Type = v
+	} else if i, e := strconv.Atoi(l.token); e != nil {
+		return nil, &ParseError{f, "bad CERT Type", l}, ""
+	} else {
+		rr.Type = uint16(i)
+	}
+	<-c     // zBlank
+	l = <-c // zString
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad CERT KeyTag", l}, ""
+	}
+	rr.KeyTag = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	if v, ok := StringToAlgorithm[l.token]; ok {
+		rr.Algorithm = v
+	} else if i, e := strconv.Atoi(l.token); e != nil {
+		return nil, &ParseError{f, "bad CERT Algorithm", l}, ""
+	} else {
+		rr.Algorithm = uint8(i)
+	}
+	s, e1, c1 := endingToString(c, "bad CERT Certificate", f)
+	if e1 != nil {
+		return nil, e1, c1
+	}
+	rr.Certificate = s
+	return rr, nil, c1
+}
+
+func setOPENPGPKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(OPENPGPKEY)
+	rr.Hdr = h
+
+	s, e, c1 := endingToString(c, "bad OPENPGPKEY PublicKey", f)
+	if e != nil {
+		return nil, e, c1
+	}
+	rr.PublicKey = s
+	return rr, nil, c1
+}
+
+func setSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setRRSIG(h, c, o, f)
+	if r != nil {
+		return &SIG{*r.(*RRSIG)}, e, s
+	}
+	return nil, e, s
+}
+
+func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(RRSIG)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	if t, ok := StringToType[l.tokenUpper]; !ok {
+		if strings.HasPrefix(l.tokenUpper, "TYPE") {
+			t, ok = typeToInt(l.tokenUpper)
+			if !ok {
+				return nil, &ParseError{f, "bad RRSIG Typecovered", l}, ""
+			}
+			rr.TypeCovered = t
+		} else {
+			return nil, &ParseError{f, "bad RRSIG Typecovered", l}, ""
+		}
+	} else {
+		rr.TypeCovered = t
+	}
+	<-c // zBlank
+	l = <-c
+	i, err := strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad RRSIG Algorithm", l}, ""
+	}
+	rr.Algorithm = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, err = strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad RRSIG Labels", l}, ""
+	}
+	rr.Labels = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, err = strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, ""
+	}
+	rr.OrigTtl = uint32(i)
+	<-c // zBlank
+	l = <-c
+	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 nil, &ParseError{f, "bad RRSIG Expiration", l}, ""
+		}
+	} else {
+		rr.Expiration = i
+	}
+	<-c // zBlank
+	l = <-c
+	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 nil, &ParseError{f, "bad RRSIG Inception", l}, ""
+		}
+	} else {
+		rr.Inception = i
+	}
+	<-c // zBlank
+	l = <-c
+	i, err = strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad RRSIG KeyTag", l}, ""
+	}
+	rr.KeyTag = uint16(i)
+	<-c // zBlank
+	l = <-c
+	rr.SignerName = l.token
+	if l.token == "@" {
+		rr.SignerName = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad RRSIG SignerName", l}, ""
+		}
+		if rr.SignerName[l.length-1] != '.' {
+			rr.SignerName = appendOrigin(rr.SignerName, o)
+		}
+	}
+	s, e, c1 := endingToString(c, "bad RRSIG Signature", f)
+	if e != nil {
+		return nil, e, c1
+	}
+	rr.Signature = s
+	return rr, nil, c1
+}
+
+func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NSEC)
+	rr.Hdr = h
+
+	l := <-c
+	rr.NextDomain = l.token
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	if l.token == "@" {
+		rr.NextDomain = o
+	} else {
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad NSEC NextDomain", l}, ""
+		}
+		if rr.NextDomain[l.length-1] != '.' {
+			rr.NextDomain = appendOrigin(rr.NextDomain, o)
+		}
+	}
+
+	rr.TypeBitMap = make([]uint16, 0)
+	var (
+		k  uint16
+		ok bool
+	)
+	l = <-c
+	for l.value != zNewline && l.value != zEOF {
+		switch l.value {
+		case zBlank:
+			// Ok
+		case zString:
+			if k, ok = StringToType[l.tokenUpper]; !ok {
+				if k, ok = typeToInt(l.tokenUpper); !ok {
+					return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, ""
+				}
+			}
+			rr.TypeBitMap = append(rr.TypeBitMap, k)
+		default:
+			return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, ""
+		}
+		l = <-c
+	}
+	return rr, nil, l.comment
+}
+
+func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NSEC3)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NSEC3 Hash", l}, ""
+	}
+	rr.Hash = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NSEC3 Flags", l}, ""
+	}
+	rr.Flags = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NSEC3 Iterations", l}, ""
+	}
+	rr.Iterations = uint16(i)
+	<-c
+	l = <-c
+	if len(l.token) == 0 || l.err {
+		return nil, &ParseError{f, "bad NSEC3 Salt", l}, ""
+	}
+	rr.SaltLength = uint8(len(l.token)) / 2
+	rr.Salt = l.token
+
+	<-c
+	l = <-c
+	if len(l.token) == 0 || l.err {
+		return nil, &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
+	for l.value != zNewline && l.value != zEOF {
+		switch l.value {
+		case zBlank:
+			// Ok
+		case zString:
+			if k, ok = StringToType[l.tokenUpper]; !ok {
+				if k, ok = typeToInt(l.tokenUpper); !ok {
+					return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, ""
+				}
+			}
+			rr.TypeBitMap = append(rr.TypeBitMap, k)
+		default:
+			return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, ""
+		}
+		l = <-c
+	}
+	return rr, nil, l.comment
+}
+
+func setNSEC3PARAM(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NSEC3PARAM)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NSEC3PARAM Hash", l}, ""
+	}
+	rr.Hash = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NSEC3PARAM Flags", l}, ""
+	}
+	rr.Flags = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l}, ""
+	}
+	rr.Iterations = uint16(i)
+	<-c
+	l = <-c
+	rr.SaltLength = uint8(len(l.token))
+	rr.Salt = l.token
+	return rr, nil, ""
+}
+
+func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(EUI48)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.length != 17 || l.err {
+		return nil, &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 nil, &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 nil, &ParseError{f, "bad EUI48 Address", l}, ""
+	}
+	rr.Address = i
+	return rr, nil, ""
+}
+
+func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(EUI64)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.length != 23 || l.err {
+		return nil, &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 nil, &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 nil, &ParseError{f, "bad EUI68 Address", l}, ""
+	}
+	rr.Address = uint64(i)
+	return rr, nil, ""
+}
+
+func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(WKS)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	rr.Address = net.ParseIP(l.token)
+	if rr.Address == nil || l.err {
+		return nil, &ParseError{f, "bad WKS Address", l}, ""
+	}
+
+	<-c // zBlank
+	l = <-c
+	proto := "tcp"
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad WKS Protocol", l}, ""
+	}
+	rr.Protocol = uint8(i)
+	switch rr.Protocol {
+	case 17:
+		proto = "udp"
+	case 6:
+		proto = "tcp"
+	default:
+		return nil, &ParseError{f, "bad WKS Protocol", l}, ""
+	}
+
+	<-c
+	l = <-c
+	rr.BitMap = make([]uint16, 0)
+	var (
+		k   int
+		err error
+	)
+	for l.value != zNewline && l.value != zEOF {
+		switch l.value {
+		case zBlank:
+			// Ok
+		case zString:
+			if k, err = net.LookupPort(proto, l.token); err != nil {
+				i, e := strconv.Atoi(l.token) // If a number use that
+				if e != nil {
+					return nil, &ParseError{f, "bad WKS BitMap", l}, ""
+				}
+				rr.BitMap = append(rr.BitMap, uint16(i))
+			}
+			rr.BitMap = append(rr.BitMap, uint16(k))
+		default:
+			return nil, &ParseError{f, "bad WKS BitMap", l}, ""
+		}
+		l = <-c
+	}
+	return rr, nil, l.comment
+}
+
+func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(SSHFP)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad SSHFP Algorithm", l}, ""
+	}
+	rr.Algorithm = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad SSHFP Type", l}, ""
+	}
+	rr.Type = uint8(i)
+	<-c // zBlank
+	s, e1, c1 := endingToString(c, "bad SSHFP Fingerprint", f)
+	if e1 != nil {
+		return nil, e1, c1
+	}
+	rr.FingerPrint = s
+	return rr, nil, ""
+}
+
+func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) {
+	rr := new(DNSKEY)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad " + typ + " Flags", l}, ""
+	}
+	rr.Flags = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad " + typ + " Protocol", l}, ""
+	}
+	rr.Protocol = uint8(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, ""
+	}
+	rr.Algorithm = uint8(i)
+	s, e1, c1 := endingToString(c, "bad "+typ+" PublicKey", f)
+	if e1 != nil {
+		return nil, e1, c1
+	}
+	rr.PublicKey = s
+	return rr, nil, c1
+}
+
+func setKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setDNSKEYs(h, c, o, f, "KEY")
+	if r != nil {
+		return &KEY{*r.(*DNSKEY)}, e, s
+	}
+	return nil, e, s
+}
+
+func setDNSKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setDNSKEYs(h, c, o, f, "DNSKEY")
+	return r, e, s
+}
+
+func setCDNSKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setDNSKEYs(h, c, o, f, "CDNSKEY")
+	if r != nil {
+		return &CDNSKEY{*r.(*DNSKEY)}, e, s
+	}
+	return nil, e, s
+}
+
+func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(RKEY)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad RKEY Flags", l}, ""
+	}
+	rr.Flags = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad RKEY Protocol", l}, ""
+	}
+	rr.Protocol = uint8(i)
+	<-c     // zBlank
+	l = <-c // zString
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad RKEY Algorithm", l}, ""
+	}
+	rr.Algorithm = uint8(i)
+	s, e1, c1 := endingToString(c, "bad RKEY PublicKey", f)
+	if e1 != nil {
+		return nil, e1, c1
+	}
+	rr.PublicKey = s
+	return rr, nil, c1
+}
+
+func setEID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(EID)
+	rr.Hdr = h
+	s, e, c1 := endingToString(c, "bad EID Endpoint", f)
+	if e != nil {
+		return nil, e, c1
+	}
+	rr.Endpoint = s
+	return rr, nil, c1
+}
+
+func setNIMLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NIMLOC)
+	rr.Hdr = h
+	s, e, c1 := endingToString(c, "bad NIMLOC Locator", f)
+	if e != nil {
+		return nil, e, c1
+	}
+	rr.Locator = s
+	return rr, nil, c1
+}
+
+func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(GPOS)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	_, e := strconv.ParseFloat(l.token, 64)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad GPOS Longitude", l}, ""
+	}
+	rr.Longitude = l.token
+	<-c // zBlank
+	l = <-c
+	_, e = strconv.ParseFloat(l.token, 64)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad GPOS Latitude", l}, ""
+	}
+	rr.Latitude = l.token
+	<-c // zBlank
+	l = <-c
+	_, e = strconv.ParseFloat(l.token, 64)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad GPOS Altitude", l}, ""
+	}
+	rr.Altitude = l.token
+	return rr, nil, ""
+}
+
+func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) {
+	rr := new(DS)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad " + typ + " KeyTag", l}, ""
+	}
+	rr.KeyTag = uint16(i)
+	<-c // zBlank
+	l = <-c
+	if i, e := strconv.Atoi(l.token); e != nil {
+		i, ok := StringToAlgorithm[l.tokenUpper]
+		if !ok || l.err {
+			return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, ""
+		}
+		rr.Algorithm = i
+	} else {
+		rr.Algorithm = uint8(i)
+	}
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad " + typ + " DigestType", l}, ""
+	}
+	rr.DigestType = uint8(i)
+	s, e1, c1 := endingToString(c, "bad "+typ+" Digest", f)
+	if e1 != nil {
+		return nil, e1, c1
+	}
+	rr.Digest = s
+	return rr, nil, c1
+}
+
+func setDS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setDSs(h, c, o, f, "DS")
+	return r, e, s
+}
+
+func setDLV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setDSs(h, c, o, f, "DLV")
+	if r != nil {
+		return &DLV{*r.(*DS)}, e, s
+	}
+	return nil, e, s
+}
+
+func setCDS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	r, e, s := setDSs(h, c, o, f, "CDS")
+	if r != nil {
+		return &CDS{*r.(*DS)}, e, s
+	}
+	return nil, e, s
+}
+
+func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(TA)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad TA KeyTag", l}, ""
+	}
+	rr.KeyTag = uint16(i)
+	<-c // zBlank
+	l = <-c
+	if i, e := strconv.Atoi(l.token); e != nil {
+		i, ok := StringToAlgorithm[l.tokenUpper]
+		if !ok || l.err {
+			return nil, &ParseError{f, "bad TA Algorithm", l}, ""
+		}
+		rr.Algorithm = i
+	} else {
+		rr.Algorithm = uint8(i)
+	}
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad TA DigestType", l}, ""
+	}
+	rr.DigestType = uint8(i)
+	s, e, c1 := endingToString(c, "bad TA Digest", f)
+	if e != nil {
+		return nil, e.(*ParseError), c1
+	}
+	rr.Digest = s
+	return rr, nil, c1
+}
+
+func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(TLSA)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad TLSA Usage", l}, ""
+	}
+	rr.Usage = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad TLSA Selector", l}, ""
+	}
+	rr.Selector = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad TLSA MatchingType", l}, ""
+	}
+	rr.MatchingType = uint8(i)
+	// So this needs be e2 (i.e. different than e), because...??t
+	s, e2, c1 := endingToString(c, "bad TLSA Certificate", f)
+	if e2 != nil {
+		return nil, e2, c1
+	}
+	rr.Certificate = s
+	return rr, nil, c1
+}
+
+func setRFC3597(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(RFC3597)
+	rr.Hdr = h
+	l := <-c
+	if l.token != "\\#" {
+		return nil, &ParseError{f, "bad RFC3597 Rdata", l}, ""
+	}
+	<-c // zBlank
+	l = <-c
+	rdlength, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, ""
+	}
+
+	s, e1, c1 := endingToString(c, "bad RFC3597 Rdata", f)
+	if e1 != nil {
+		return nil, e1, c1
+	}
+	if rdlength*2 != len(s) {
+		return nil, &ParseError{f, "bad RFC3597 Rdata", l}, ""
+	}
+	rr.Rdata = s
+	return rr, nil, c1
+}
+
+func setSPF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(SPF)
+	rr.Hdr = h
+
+	s, e, c1 := endingToTxtSlice(c, "bad SPF Txt", f)
+	if e != nil {
+		return nil, e, ""
+	}
+	rr.Txt = s
+	return rr, nil, c1
+}
+
+func setTXT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(TXT)
+	rr.Hdr = h
+
+	// no zBlank reading here, because all this rdata is TXT
+	s, e, c1 := endingToTxtSlice(c, "bad TXT Txt", f)
+	if e != nil {
+		return nil, e, ""
+	}
+	rr.Txt = s
+	return rr, nil, c1
+}
+
+// identical to setTXT
+func setNINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NINFO)
+	rr.Hdr = h
+
+	s, e, c1 := endingToTxtSlice(c, "bad NINFO ZSData", f)
+	if e != nil {
+		return nil, e, ""
+	}
+	rr.ZSData = s
+	return rr, nil, c1
+}
+
+func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(URI)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 { // Dynamic updates.
+		return rr, nil, ""
+	}
+
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad URI Priority", l}, ""
+	}
+	rr.Priority = uint16(i)
+	<-c // zBlank
+	l = <-c
+	i, e = strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad URI Weight", l}, ""
+	}
+	rr.Weight = uint16(i)
+
+	<-c // zBlank
+	s, err, c1 := endingToTxtSlice(c, "bad URI Target", f)
+	if err != nil {
+		return nil, err, ""
+	}
+	if len(s) > 1 {
+		return nil, &ParseError{f, "bad URI Target", l}, ""
+	}
+	rr.Target = s[0]
+	return rr, nil, c1
+}
+
+func setDHCID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	// awesome record to parse!
+	rr := new(DHCID)
+	rr.Hdr = h
+
+	s, e, c1 := endingToString(c, "bad DHCID Digest", f)
+	if e != nil {
+		return nil, e, c1
+	}
+	rr.Digest = s
+	return rr, nil, c1
+}
+
+func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(NID)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad NID Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	u, err := stringToNodeID(l)
+	if err != nil || l.err {
+		return nil, err, ""
+	}
+	rr.NodeID = u
+	return rr, nil, ""
+}
+
+func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(L32)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad L32 Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Locator32 = net.ParseIP(l.token)
+	if rr.Locator32 == nil || l.err {
+		return nil, &ParseError{f, "bad L32 Locator", l}, ""
+	}
+	return rr, nil, ""
+}
+
+func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(LP)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad LP Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Fqdn = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Fqdn = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad LP Fqdn", l}, ""
+	}
+	if rr.Fqdn[l.length-1] != '.' {
+		rr.Fqdn = appendOrigin(rr.Fqdn, o)
+	}
+	return rr, nil, ""
+}
+
+func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(L64)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad L64 Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	u, err := stringToNodeID(l)
+	if err != nil || l.err {
+		return nil, err, ""
+	}
+	rr.Locator64 = u
+	return rr, nil, ""
+}
+
+func setUID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(UID)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad UID Uid", l}, ""
+	}
+	rr.Uid = uint32(i)
+	return rr, nil, ""
+}
+
+func setGID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(GID)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad GID Gid", l}, ""
+	}
+	rr.Gid = uint32(i)
+	return rr, nil, ""
+}
+
+func setUINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(UINFO)
+	rr.Hdr = h
+	s, e, c1 := endingToTxtSlice(c, "bad UINFO Uinfo", f)
+	if e != nil {
+		return nil, e, ""
+	}
+	rr.Uinfo = s[0] // silently discard anything above
+	return rr, nil, c1
+}
+
+func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(PX)
+	rr.Hdr = h
+
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	i, e := strconv.Atoi(l.token)
+	if e != nil || l.err {
+		return nil, &ParseError{f, "bad PX Preference", l}, ""
+	}
+	rr.Preference = uint16(i)
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Map822 = l.token
+	if l.length == 0 {
+		return rr, nil, ""
+	}
+	if l.token == "@" {
+		rr.Map822 = o
+		return rr, nil, ""
+	}
+	_, ok := IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad PX Map822", l}, ""
+	}
+	if rr.Map822[l.length-1] != '.' {
+		rr.Map822 = appendOrigin(rr.Map822, o)
+	}
+	<-c     // zBlank
+	l = <-c // zString
+	rr.Mapx400 = l.token
+	if l.token == "@" {
+		rr.Mapx400 = o
+		return rr, nil, ""
+	}
+	_, ok = IsDomainName(l.token)
+	if !ok || l.length == 0 || l.err {
+		return nil, &ParseError{f, "bad PX Mapx400", l}, ""
+	}
+	if rr.Mapx400[l.length-1] != '.' {
+		rr.Mapx400 = appendOrigin(rr.Mapx400, o)
+	}
+	return rr, nil, ""
+}
+
+func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(IPSECKEY)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, err := strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, ""
+	}
+	rr.Precedence = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, err = strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, ""
+	}
+	rr.GatewayType = uint8(i)
+	<-c // zBlank
+	l = <-c
+	i, err = strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, ""
+	}
+	rr.Algorithm = uint8(i)
+
+	// Now according to GatewayType we can have different elements here
+	<-c // zBlank
+	l = <-c
+	switch rr.GatewayType {
+	case 0:
+		fallthrough
+	case 3:
+		rr.GatewayName = l.token
+		if l.token == "@" {
+			rr.GatewayName = o
+		}
+		_, ok := IsDomainName(l.token)
+		if !ok || l.length == 0 || l.err {
+			return nil, &ParseError{f, "bad IPSECKEY GatewayName", l}, ""
+		}
+		if rr.GatewayName[l.length-1] != '.' {
+			rr.GatewayName = appendOrigin(rr.GatewayName, o)
+		}
+	case 1:
+		rr.GatewayA = net.ParseIP(l.token)
+		if rr.GatewayA == nil {
+			return nil, &ParseError{f, "bad IPSECKEY GatewayA", l}, ""
+		}
+	case 2:
+		rr.GatewayAAAA = net.ParseIP(l.token)
+		if rr.GatewayAAAA == nil {
+			return nil, &ParseError{f, "bad IPSECKEY GatewayAAAA", l}, ""
+		}
+	default:
+		return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, ""
+	}
+
+	s, e, c1 := endingToString(c, "bad IPSECKEY PublicKey", f)
+	if e != nil {
+		return nil, e, c1
+	}
+	rr.PublicKey = s
+	return rr, nil, c1
+}
+
+func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
+	rr := new(CAA)
+	rr.Hdr = h
+	l := <-c
+	if l.length == 0 {
+		return rr, nil, l.comment
+	}
+	i, err := strconv.Atoi(l.token)
+	if err != nil || l.err {
+		return nil, &ParseError{f, "bad CAA Flag", l}, ""
+	}
+	rr.Flag = uint8(i)
+
+	<-c     // zBlank
+	l = <-c // zString
+	if l.value != zString {
+		return nil, &ParseError{f, "bad CAA Tag", l}, ""
+	}
+	rr.Tag = l.token
+
+	<-c // zBlank
+	s, e, c1 := endingToTxtSlice(c, "bad CAA Value", f)
+	if e != nil {
+		return nil, e, ""
+	}
+	if len(s) > 1 {
+		return nil, &ParseError{f, "bad CAA Value", l}, ""
+	}
+	rr.Value = s[0]
+	return rr, nil, c1
+}
+
+var typeToparserFunc = map[uint16]parserFunc{
+	TypeAAAA:       parserFunc{setAAAA, false},
+	TypeAFSDB:      parserFunc{setAFSDB, false},
+	TypeA:          parserFunc{setA, false},
+	TypeCAA:        parserFunc{setCAA, true},
+	TypeCDS:        parserFunc{setCDS, true},
+	TypeCDNSKEY:    parserFunc{setCDNSKEY, true},
+	TypeCERT:       parserFunc{setCERT, true},
+	TypeCNAME:      parserFunc{setCNAME, false},
+	TypeDHCID:      parserFunc{setDHCID, true},
+	TypeDLV:        parserFunc{setDLV, true},
+	TypeDNAME:      parserFunc{setDNAME, false},
+	TypeKEY:        parserFunc{setKEY, true},
+	TypeDNSKEY:     parserFunc{setDNSKEY, true},
+	TypeDS:         parserFunc{setDS, true},
+	TypeEID:        parserFunc{setEID, true},
+	TypeEUI48:      parserFunc{setEUI48, false},
+	TypeEUI64:      parserFunc{setEUI64, false},
+	TypeGID:        parserFunc{setGID, false},
+	TypeGPOS:       parserFunc{setGPOS, false},
+	TypeHINFO:      parserFunc{setHINFO, true},
+	TypeHIP:        parserFunc{setHIP, true},
+	TypeIPSECKEY:   parserFunc{setIPSECKEY, true},
+	TypeKX:         parserFunc{setKX, false},
+	TypeL32:        parserFunc{setL32, false},
+	TypeL64:        parserFunc{setL64, false},
+	TypeLOC:        parserFunc{setLOC, true},
+	TypeLP:         parserFunc{setLP, false},
+	TypeMB:         parserFunc{setMB, false},
+	TypeMD:         parserFunc{setMD, false},
+	TypeMF:         parserFunc{setMF, false},
+	TypeMG:         parserFunc{setMG, false},
+	TypeMINFO:      parserFunc{setMINFO, false},
+	TypeMR:         parserFunc{setMR, false},
+	TypeMX:         parserFunc{setMX, false},
+	TypeNAPTR:      parserFunc{setNAPTR, false},
+	TypeNID:        parserFunc{setNID, false},
+	TypeNIMLOC:     parserFunc{setNIMLOC, true},
+	TypeNINFO:      parserFunc{setNINFO, true},
+	TypeNSAPPTR:    parserFunc{setNSAPPTR, false},
+	TypeNSEC3PARAM: parserFunc{setNSEC3PARAM, false},
+	TypeNSEC3:      parserFunc{setNSEC3, true},
+	TypeNSEC:       parserFunc{setNSEC, true},
+	TypeNS:         parserFunc{setNS, false},
+	TypeOPENPGPKEY: parserFunc{setOPENPGPKEY, true},
+	TypePTR:        parserFunc{setPTR, false},
+	TypePX:         parserFunc{setPX, false},
+	TypeSIG:        parserFunc{setSIG, true},
+	TypeRKEY:       parserFunc{setRKEY, true},
+	TypeRP:         parserFunc{setRP, false},
+	TypeRRSIG:      parserFunc{setRRSIG, true},
+	TypeRT:         parserFunc{setRT, false},
+	TypeSOA:        parserFunc{setSOA, false},
+	TypeSPF:        parserFunc{setSPF, true},
+	TypeSRV:        parserFunc{setSRV, false},
+	TypeSSHFP:      parserFunc{setSSHFP, true},
+	TypeTALINK:     parserFunc{setTALINK, false},
+	TypeTA:         parserFunc{setTA, true},
+	TypeTLSA:       parserFunc{setTLSA, true},
+	TypeTXT:        parserFunc{setTXT, true},
+	TypeUID:        parserFunc{setUID, false},
+	TypeUINFO:      parserFunc{setUINFO, true},
+	TypeURI:        parserFunc{setURI, true},
+	TypeWKS:        parserFunc{setWKS, true},
+	TypeX25:        parserFunc{setX25, false},
+}

+ 842 - 0
vendor/src/github.com/miekg/dns/ztypes.go

@@ -0,0 +1,842 @@
+// *** DO NOT MODIFY ***
+// AUTOGENERATED BY go generate
+
+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) },
+	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) },
+	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) },
+	TypeIPSECKEY:   func() RR { return new(IPSECKEY) },
+	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) },
+	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) },
+	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) },
+	TypeWKS:        func() RR { return new(WKS) },
+	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",
+	TypeAXFR:       "AXFR",
+	TypeCAA:        "CAA",
+	TypeCDNSKEY:    "CDNSKEY",
+	TypeCDS:        "CDS",
+	TypeCERT:       "CERT",
+	TypeCNAME:      "CNAME",
+	TypeDHCID:      "DHCID",
+	TypeDLV:        "DLV",
+	TypeDNAME:      "DNAME",
+	TypeDNSKEY:     "DNSKEY",
+	TypeDS:         "DS",
+	TypeEID:        "EID",
+	TypeEUI48:      "EUI48",
+	TypeEUI64:      "EUI64",
+	TypeGID:        "GID",
+	TypeGPOS:       "GPOS",
+	TypeHINFO:      "HINFO",
+	TypeHIP:        "HIP",
+	TypeIPSECKEY:   "IPSECKEY",
+	TypeISDN:       "ISDN",
+	TypeIXFR:       "IXFR",
+	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",
+	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",
+	TypeWKS:        "WKS",
+	TypeX25:        "X25",
+	TypeNSAPPTR:    "NSAP-PTR",
+}
+
+// Header() functions
+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 *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 *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 *IPSECKEY) 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 *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 *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 *WKS) Header() *RR_Header        { return &rr.Hdr }
+func (rr *X25) Header() *RR_Header        { return &rr.Hdr }
+
+// len() functions
+func (rr *A) len() int {
+	l := rr.Hdr.len()
+	l += net.IPv4len // A
+	return l
+}
+func (rr *AAAA) len() int {
+	l := rr.Hdr.len()
+	l += net.IPv6len // AAAA
+	return l
+}
+func (rr *AFSDB) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Subtype
+	l += len(rr.Hostname) + 1
+	return l
+}
+func (rr *ANY) len() int {
+	l := rr.Hdr.len()
+	return l
+}
+func (rr *CAA) len() int {
+	l := rr.Hdr.len()
+	l += 1 // Flag
+	l += len(rr.Tag) + 1
+	l += len(rr.Value)
+	return l
+}
+func (rr *CERT) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Type
+	l += 2 // KeyTag
+	l += 1 // Algorithm
+	l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
+	return l
+}
+func (rr *CNAME) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Target) + 1
+	return l
+}
+func (rr *DHCID) len() int {
+	l := rr.Hdr.len()
+	l += base64.StdEncoding.DecodedLen(len(rr.Digest))
+	return l
+}
+func (rr *DNAME) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Target) + 1
+	return l
+}
+func (rr *DNSKEY) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Flags
+	l += 1 // Protocol
+	l += 1 // Algorithm
+	l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+	return l
+}
+func (rr *DS) len() int {
+	l := rr.Hdr.len()
+	l += 2 // KeyTag
+	l += 1 // Algorithm
+	l += 1 // DigestType
+	l += len(rr.Digest)/2 + 1
+	return l
+}
+func (rr *EID) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Endpoint)/2 + 1
+	return l
+}
+func (rr *EUI48) len() int {
+	l := rr.Hdr.len()
+	l += 6 // Address
+	return l
+}
+func (rr *EUI64) len() int {
+	l := rr.Hdr.len()
+	l += 8 // Address
+	return l
+}
+func (rr *GID) len() int {
+	l := rr.Hdr.len()
+	l += 4 // Gid
+	return l
+}
+func (rr *GPOS) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Longitude) + 1
+	l += len(rr.Latitude) + 1
+	l += len(rr.Altitude) + 1
+	return l
+}
+func (rr *HINFO) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Cpu) + 1
+	l += len(rr.Os) + 1
+	return l
+}
+func (rr *HIP) len() int {
+	l := rr.Hdr.len()
+	l += 1 // HitLength
+	l += 1 // PublicKeyAlgorithm
+	l += 2 // PublicKeyLength
+	l += len(rr.Hit)/2 + 1
+	l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+	for _, x := range rr.RendezvousServers {
+		l += len(x) + 1
+	}
+	return l
+}
+func (rr *KX) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += len(rr.Exchanger) + 1
+	return l
+}
+func (rr *L32) len() int {
+	l := rr.Hdr.len()
+	l += 2           // Preference
+	l += net.IPv4len // Locator32
+	return l
+}
+func (rr *L64) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += 8 // Locator64
+	return l
+}
+func (rr *LOC) len() int {
+	l := rr.Hdr.len()
+	l += 1 // Version
+	l += 1 // Size
+	l += 1 // HorizPre
+	l += 1 // VertPre
+	l += 4 // Latitude
+	l += 4 // Longitude
+	l += 4 // Altitude
+	return l
+}
+func (rr *LP) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += len(rr.Fqdn) + 1
+	return l
+}
+func (rr *MB) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Mb) + 1
+	return l
+}
+func (rr *MD) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Md) + 1
+	return l
+}
+func (rr *MF) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Mf) + 1
+	return l
+}
+func (rr *MG) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Mg) + 1
+	return l
+}
+func (rr *MINFO) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Rmail) + 1
+	l += len(rr.Email) + 1
+	return l
+}
+func (rr *MR) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Mr) + 1
+	return l
+}
+func (rr *MX) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += len(rr.Mx) + 1
+	return l
+}
+func (rr *NAPTR) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Order
+	l += 2 // Preference
+	l += len(rr.Flags) + 1
+	l += len(rr.Service) + 1
+	l += len(rr.Regexp) + 1
+	l += len(rr.Replacement) + 1
+	return l
+}
+func (rr *NID) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += 8 // NodeID
+	return l
+}
+func (rr *NIMLOC) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Locator)/2 + 1
+	return l
+}
+func (rr *NINFO) len() int {
+	l := rr.Hdr.len()
+	for _, x := range rr.ZSData {
+		l += len(x) + 1
+	}
+	return l
+}
+func (rr *NS) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Ns) + 1
+	return l
+}
+func (rr *NSAPPTR) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Ptr) + 1
+	return l
+}
+func (rr *NSEC3PARAM) len() int {
+	l := rr.Hdr.len()
+	l += 1 // Hash
+	l += 1 // Flags
+	l += 2 // Iterations
+	l += 1 // SaltLength
+	l += len(rr.Salt)/2 + 1
+	return l
+}
+func (rr *OPENPGPKEY) len() int {
+	l := rr.Hdr.len()
+	l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+	return l
+}
+func (rr *PTR) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Ptr) + 1
+	return l
+}
+func (rr *PX) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += len(rr.Map822) + 1
+	l += len(rr.Mapx400) + 1
+	return l
+}
+func (rr *RFC3597) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Rdata)/2 + 1
+	return l
+}
+func (rr *RKEY) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Flags
+	l += 1 // Protocol
+	l += 1 // Algorithm
+	l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
+	return l
+}
+func (rr *RP) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Mbox) + 1
+	l += len(rr.Txt) + 1
+	return l
+}
+func (rr *RRSIG) len() int {
+	l := rr.Hdr.len()
+	l += 2 // TypeCovered
+	l += 1 // Algorithm
+	l += 1 // Labels
+	l += 4 // OrigTtl
+	l += 4 // Expiration
+	l += 4 // Inception
+	l += 2 // KeyTag
+	l += len(rr.SignerName) + 1
+	l += base64.StdEncoding.DecodedLen(len(rr.Signature))
+	return l
+}
+func (rr *RT) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Preference
+	l += len(rr.Host) + 1
+	return l
+}
+func (rr *SOA) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Ns) + 1
+	l += len(rr.Mbox) + 1
+	l += 4 // Serial
+	l += 4 // Refresh
+	l += 4 // Retry
+	l += 4 // Expire
+	l += 4 // Minttl
+	return l
+}
+func (rr *SPF) len() int {
+	l := rr.Hdr.len()
+	for _, x := range rr.Txt {
+		l += len(x) + 1
+	}
+	return l
+}
+func (rr *SRV) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Priority
+	l += 2 // Weight
+	l += 2 // Port
+	l += len(rr.Target) + 1
+	return l
+}
+func (rr *SSHFP) len() int {
+	l := rr.Hdr.len()
+	l += 1 // Algorithm
+	l += 1 // Type
+	l += len(rr.FingerPrint)/2 + 1
+	return l
+}
+func (rr *TA) len() int {
+	l := rr.Hdr.len()
+	l += 2 // KeyTag
+	l += 1 // Algorithm
+	l += 1 // DigestType
+	l += len(rr.Digest)/2 + 1
+	return l
+}
+func (rr *TALINK) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.PreviousName) + 1
+	l += len(rr.NextName) + 1
+	return l
+}
+func (rr *TKEY) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Algorithm) + 1
+	l += 4 // Inception
+	l += 4 // Expiration
+	l += 2 // Mode
+	l += 2 // Error
+	l += 2 // KeySize
+	l += len(rr.Key) + 1
+	l += 2 // OtherLen
+	l += len(rr.OtherData) + 1
+	return l
+}
+func (rr *TLSA) len() int {
+	l := rr.Hdr.len()
+	l += 1 // Usage
+	l += 1 // Selector
+	l += 1 // MatchingType
+	l += len(rr.Certificate)/2 + 1
+	return l
+}
+func (rr *TSIG) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Algorithm) + 1
+	l += 6 // TimeSigned
+	l += 2 // Fudge
+	l += 2 // MACSize
+	l += len(rr.MAC)/2 + 1
+	l += 2 // OrigId
+	l += 2 // Error
+	l += 2 // OtherLen
+	l += len(rr.OtherData)/2 + 1
+	return l
+}
+func (rr *TXT) len() int {
+	l := rr.Hdr.len()
+	for _, x := range rr.Txt {
+		l += len(x) + 1
+	}
+	return l
+}
+func (rr *UID) len() int {
+	l := rr.Hdr.len()
+	l += 4 // Uid
+	return l
+}
+func (rr *UINFO) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.Uinfo) + 1
+	return l
+}
+func (rr *URI) len() int {
+	l := rr.Hdr.len()
+	l += 2 // Priority
+	l += 2 // Weight
+	l += len(rr.Target)
+	return l
+}
+func (rr *X25) len() int {
+	l := rr.Hdr.len()
+	l += len(rr.PSDNAddress) + 1
+	return l
+}
+
+// copy() functions
+func (rr *A) copy() RR {
+	return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)}
+}
+func (rr *AAAA) copy() RR {
+	return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)}
+}
+func (rr *AFSDB) copy() RR {
+	return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname}
+}
+func (rr *ANY) copy() RR {
+	return &ANY{*rr.Hdr.copyHeader()}
+}
+func (rr *CAA) copy() RR {
+	return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value}
+}
+func (rr *CERT) copy() RR {
+	return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
+}
+func (rr *CNAME) copy() RR {
+	return &CNAME{*rr.Hdr.copyHeader(), rr.Target}
+}
+func (rr *DHCID) copy() RR {
+	return &DHCID{*rr.Hdr.copyHeader(), rr.Digest}
+}
+func (rr *DNAME) copy() RR {
+	return &DNAME{*rr.Hdr.copyHeader(), rr.Target}
+}
+func (rr *DNSKEY) copy() RR {
+	return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
+}
+func (rr *DS) copy() RR {
+	return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
+}
+func (rr *EID) copy() RR {
+	return &EID{*rr.Hdr.copyHeader(), rr.Endpoint}
+}
+func (rr *EUI48) copy() RR {
+	return &EUI48{*rr.Hdr.copyHeader(), rr.Address}
+}
+func (rr *EUI64) copy() RR {
+	return &EUI64{*rr.Hdr.copyHeader(), rr.Address}
+}
+func (rr *GID) copy() RR {
+	return &GID{*rr.Hdr.copyHeader(), rr.Gid}
+}
+func (rr *GPOS) copy() RR {
+	return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude}
+}
+func (rr *HINFO) copy() RR {
+	return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os}
+}
+func (rr *HIP) copy() RR {
+	RendezvousServers := make([]string, len(rr.RendezvousServers))
+	copy(RendezvousServers, rr.RendezvousServers)
+	return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
+}
+func (rr *IPSECKEY) copy() RR {
+	return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, copyIP(rr.GatewayA), copyIP(rr.GatewayAAAA), rr.GatewayName, rr.PublicKey}
+}
+func (rr *KX) copy() RR {
+	return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger}
+}
+func (rr *L32) copy() RR {
+	return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)}
+}
+func (rr *L64) copy() RR {
+	return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64}
+}
+func (rr *LOC) copy() RR {
+	return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
+}
+func (rr *LP) copy() RR {
+	return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn}
+}
+func (rr *MB) copy() RR {
+	return &MB{*rr.Hdr.copyHeader(), rr.Mb}
+}
+func (rr *MD) copy() RR {
+	return &MD{*rr.Hdr.copyHeader(), rr.Md}
+}
+func (rr *MF) copy() RR {
+	return &MF{*rr.Hdr.copyHeader(), rr.Mf}
+}
+func (rr *MG) copy() RR {
+	return &MG{*rr.Hdr.copyHeader(), rr.Mg}
+}
+func (rr *MINFO) copy() RR {
+	return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email}
+}
+func (rr *MR) copy() RR {
+	return &MR{*rr.Hdr.copyHeader(), rr.Mr}
+}
+func (rr *MX) copy() RR {
+	return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx}
+}
+func (rr *NAPTR) copy() RR {
+	return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
+}
+func (rr *NID) copy() RR {
+	return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID}
+}
+func (rr *NIMLOC) copy() RR {
+	return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator}
+}
+func (rr *NINFO) copy() RR {
+	ZSData := make([]string, len(rr.ZSData))
+	copy(ZSData, rr.ZSData)
+	return &NINFO{*rr.Hdr.copyHeader(), ZSData}
+}
+func (rr *NS) copy() RR {
+	return &NS{*rr.Hdr.copyHeader(), rr.Ns}
+}
+func (rr *NSAPPTR) copy() RR {
+	return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr}
+}
+func (rr *NSEC) copy() RR {
+	TypeBitMap := make([]uint16, len(rr.TypeBitMap))
+	copy(TypeBitMap, rr.TypeBitMap)
+	return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, TypeBitMap}
+}
+func (rr *NSEC3) copy() RR {
+	TypeBitMap := make([]uint16, len(rr.TypeBitMap))
+	copy(TypeBitMap, rr.TypeBitMap)
+	return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
+}
+func (rr *NSEC3PARAM) copy() RR {
+	return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
+}
+func (rr *OPENPGPKEY) copy() RR {
+	return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey}
+}
+func (rr *OPT) copy() RR {
+	Option := make([]EDNS0, len(rr.Option))
+	copy(Option, rr.Option)
+	return &OPT{*rr.Hdr.copyHeader(), Option}
+}
+func (rr *PTR) copy() RR {
+	return &PTR{*rr.Hdr.copyHeader(), rr.Ptr}
+}
+func (rr *PX) copy() RR {
+	return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400}
+}
+func (rr *RFC3597) copy() RR {
+	return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata}
+}
+func (rr *RKEY) copy() RR {
+	return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
+}
+func (rr *RP) copy() RR {
+	return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt}
+}
+func (rr *RRSIG) copy() RR {
+	return &RRSIG{*rr.Hdr.copyHeader(), 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.copyHeader(), rr.Preference, rr.Host}
+}
+func (rr *SOA) copy() RR {
+	return &SOA{*rr.Hdr.copyHeader(), 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.copyHeader(), Txt}
+}
+func (rr *SRV) copy() RR {
+	return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target}
+}
+func (rr *SSHFP) copy() RR {
+	return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint}
+}
+func (rr *TA) copy() RR {
+	return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
+}
+func (rr *TALINK) copy() RR {
+	return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName}
+}
+func (rr *TKEY) copy() RR {
+	return &TKEY{*rr.Hdr.copyHeader(), 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.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
+}
+func (rr *TSIG) copy() RR {
+	return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
+}
+func (rr *TXT) copy() RR {
+	Txt := make([]string, len(rr.Txt))
+	copy(Txt, rr.Txt)
+	return &TXT{*rr.Hdr.copyHeader(), Txt}
+}
+func (rr *UID) copy() RR {
+	return &UID{*rr.Hdr.copyHeader(), rr.Uid}
+}
+func (rr *UINFO) copy() RR {
+	return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo}
+}
+func (rr *URI) copy() RR {
+	return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target}
+}
+func (rr *WKS) copy() RR {
+	BitMap := make([]uint16, len(rr.BitMap))
+	copy(BitMap, rr.BitMap)
+	return &WKS{*rr.Hdr.copyHeader(), copyIP(rr.Address), rr.Protocol, BitMap}
+}
+func (rr *X25) copy() RR {
+	return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress}
+}

+ 6 - 1
vendor/src/github.com/vishvananda/netlink/.travis.yml

@@ -1,3 +1,8 @@
 language: go
+before_script:
+  # make sure we keep path in tact when we sudo
+  - sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
+  # modprobe ip_gre or else the first gre device can't be deleted
+  - sudo modprobe ip_gre
 install:
-      - go get github.com/vishvananda/netns
+  - go get github.com/vishvananda/netns

+ 1 - 1
vendor/src/github.com/vishvananda/netlink/Makefile

@@ -11,7 +11,7 @@ goroot = $(addprefix ../../../,$(1))
 unroot = $(subst ../../../,,$(1))
 fmt = $(addprefix fmt-,$(1))
 
-all: fmt
+all: fmt test
 
 $(call goroot,$(DEPS)):
 	go get $(call unroot,$@)

+ 3 - 1
vendor/src/github.com/vishvananda/netlink/addr.go

@@ -11,11 +11,13 @@ import (
 type Addr struct {
 	*net.IPNet
 	Label string
+	Flags int
+	Scope int
 }
 
 // String returns $ip/$netmask $label
 func (a Addr) String() string {
-	return fmt.Sprintf("%s %s", a.IPNet, a.Label)
+	return strings.TrimSpace(fmt.Sprintf("%s %s", a.IPNet, a.Label))
 }
 
 // ParseAddr parses the string representation of an address in the

+ 14 - 0
vendor/src/github.com/vishvananda/netlink/addr_linux.go

@@ -9,6 +9,9 @@ import (
 	"github.com/vishvananda/netlink/nl"
 )
 
+// IFA_FLAGS is a u32 attribute.
+const IFA_FLAGS = 0x8
+
 // AddrAdd will add an IP address to a link device.
 // Equivalent to: `ip addr add $addr dev $link`
 func AddrAdd(link Link, addr *Addr) error {
@@ -35,6 +38,7 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
 
 	msg := nl.NewIfAddrmsg(family)
 	msg.Index = uint32(base.Index)
+	msg.Scope = uint8(addr.Scope)
 	prefixlen, _ := addr.Mask.Size()
 	msg.Prefixlen = uint8(prefixlen)
 	req.AddData(msg)
@@ -52,6 +56,13 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
 	addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
 	req.AddData(addressData)
 
+	if addr.Flags != 0 {
+		b := make([]byte, 4)
+		native.PutUint32(b, uint32(addr.Flags))
+		flagsData := nl.NewRtAttr(IFA_FLAGS, b)
+		req.AddData(flagsData)
+	}
+
 	if addr.Label != "" {
 		labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
 		req.AddData(labelData)
@@ -111,6 +122,8 @@ func AddrList(link Link, family int) ([]Addr, error) {
 				}
 			case syscall.IFA_LABEL:
 				addr.Label = string(attr.Value[:len(attr.Value)-1])
+			case IFA_FLAGS:
+				addr.Flags = int(native.Uint32(attr.Value[0:4]))
 			}
 		}
 
@@ -120,6 +133,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
 		} else {
 			addr.IPNet = dst
 		}
+		addr.Scope = int(msg.Scope)
 
 		res = append(res, addr)
 	}

+ 39 - 15
vendor/src/github.com/vishvananda/netlink/class_linux.go

@@ -9,24 +9,38 @@ import (
 // ClassDel will delete a class from the system.
 // Equivalent to: `tc class del $class`
 func ClassDel(class Class) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_DELTCLASS, syscall.NLM_F_ACK)
-	base := class.Attrs()
-	msg := &nl.TcMsg{
-		Family:  nl.FAMILY_ALL,
-		Ifindex: int32(base.LinkIndex),
-		Handle:  base.Handle,
-		Parent:  base.Parent,
-	}
-	req.AddData(msg)
+	return classModify(syscall.RTM_DELTCLASS, 0, class)
+}
 
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+// ClassChange will change a class in place
+// Equivalent to: `tc class change $class`
+// The parent and handle MUST NOT be changed.
+
+func ClassChange(class Class) error {
+	return classModify(syscall.RTM_NEWTCLASS, 0, class)
+}
+
+// ClassReplace will replace a class to the system.
+// quivalent to: `tc class replace $class`
+// The handle MAY be changed.
+// If a class already exist with this parent/handle pair, the class is changed.
+// If a class does not already exist with this parent/handle, a new class is created.
+func ClassReplace(class Class) error {
+	return classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class)
 }
 
 // ClassAdd will add a class to the system.
 // Equivalent to: `tc class add $class`
 func ClassAdd(class Class) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return classModify(
+		syscall.RTM_NEWTCLASS,
+		syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
+		class,
+	)
+}
+
+func classModify(cmd, flags int, class Class) error {
+	req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
 	base := class.Attrs()
 	msg := &nl.TcMsg{
 		Family:  nl.FAMILY_ALL,
@@ -35,6 +49,17 @@ func ClassAdd(class Class) error {
 		Parent:  base.Parent,
 	}
 	req.AddData(msg)
+
+	if cmd != syscall.RTM_DELTCLASS {
+		if err := classPayload(req, class); err != nil {
+			return err
+		}
+	}
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
+func classPayload(req *nl.NetlinkRequest, class Class) error {
 	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
 
 	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
@@ -51,13 +76,12 @@ func ClassAdd(class Class) error {
 		nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
 	}
 	req.AddData(options)
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+	return nil
 }
 
 // ClassList gets a list of classes in the system.
 // Equivalent to: `tc class show`.
-// Generally retunrs nothing if link and parent are not specified.
+// Generally returns nothing if link and parent are not specified.
 func ClassList(link Link, parent uint32) ([]Class, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP)
 	msg := &nl.TcMsg{

+ 312 - 0
vendor/src/github.com/vishvananda/netlink/link.go

@@ -1,6 +1,7 @@
 package netlink
 
 import (
+	"fmt"
 	"net"
 	"syscall"
 )
@@ -29,6 +30,7 @@ type LinkAttrs struct {
 	ParentIndex  int         // index of the parent link device
 	MasterIndex  int         // must be the index of a bridge
 	Namespace    interface{} // nil | NsPid | NsFd
+	Alias        string
 }
 
 // NewLinkAttrs returns LinkAttrs structure filled with default values
@@ -240,6 +242,316 @@ func (ipvlan *IPVlan) Type() string {
 	return "ipvlan"
 }
 
+// BondMode type
+type BondMode int
+
+func (b BondMode) String() string {
+	s, ok := bondModeToString[b]
+	if !ok {
+		return fmt.Sprintf("BondMode(%d)", b)
+	}
+	return s
+}
+
+// StringToBondMode returns bond mode, or uknonw is the s is invalid.
+func StringToBondMode(s string) BondMode {
+	mode, ok := StringToBondModeMap[s]
+	if !ok {
+		return BOND_MODE_UNKNOWN
+	}
+	return mode
+}
+
+// Possible BondMode
+const (
+	BOND_MODE_802_3AD BondMode = iota
+	BOND_MODE_BALANCE_RR
+	BOND_MODE_ACTIVE_BACKUP
+	BOND_MODE_BALANCE_XOR
+	BOND_MODE_BROADCAST
+	BOND_MODE_BALANCE_TLB
+	BOND_MODE_BALANCE_ALB
+	BOND_MODE_UNKNOWN
+)
+
+var bondModeToString = map[BondMode]string{
+	BOND_MODE_802_3AD:       "802.3ad",
+	BOND_MODE_BALANCE_RR:    "balance-rr",
+	BOND_MODE_ACTIVE_BACKUP: "active-backup",
+	BOND_MODE_BALANCE_XOR:   "balance-xor",
+	BOND_MODE_BROADCAST:     "broadcast",
+	BOND_MODE_BALANCE_TLB:   "balance-tlb",
+	BOND_MODE_BALANCE_ALB:   "balance-alb",
+}
+var StringToBondModeMap = map[string]BondMode{
+	"802.3ad":       BOND_MODE_802_3AD,
+	"balance-rr":    BOND_MODE_BALANCE_RR,
+	"active-backup": BOND_MODE_ACTIVE_BACKUP,
+	"balance-xor":   BOND_MODE_BALANCE_XOR,
+	"broadcast":     BOND_MODE_BROADCAST,
+	"balance-tlb":   BOND_MODE_BALANCE_TLB,
+	"balance-alb":   BOND_MODE_BALANCE_ALB,
+}
+
+// BondArpValidate type
+type BondArpValidate int
+
+// Possible BondArpValidate value
+const (
+	BOND_ARP_VALIDATE_NONE BondArpValidate = iota
+	BOND_ARP_VALIDATE_ACTIVE
+	BOND_ARP_VALIDATE_BACKUP
+	BOND_ARP_VALIDATE_ALL
+)
+
+// BondPrimaryReselect type
+type BondPrimaryReselect int
+
+// Possible BondPrimaryReselect value
+const (
+	BOND_PRIMARY_RESELECT_ALWAYS BondPrimaryReselect = iota
+	BOND_PRIMARY_RESELECT_BETTER
+	BOND_PRIMARY_RESELECT_FAILURE
+)
+
+// BondArpAllTargets type
+type BondArpAllTargets int
+
+// Possible BondArpAllTargets value
+const (
+	BOND_ARP_ALL_TARGETS_ANY BondArpAllTargets = iota
+	BOND_ARP_ALL_TARGETS_ALL
+)
+
+// BondFailOverMac type
+type BondFailOverMac int
+
+// Possible BondFailOverMac value
+const (
+	BOND_FAIL_OVER_MAC_NONE BondFailOverMac = iota
+	BOND_FAIL_OVER_MAC_ACTIVE
+	BOND_FAIL_OVER_MAC_FOLLOW
+)
+
+// BondXmitHashPolicy type
+type BondXmitHashPolicy int
+
+func (b BondXmitHashPolicy) String() string {
+	s, ok := bondXmitHashPolicyToString[b]
+	if !ok {
+		return fmt.Sprintf("XmitHashPolicy(%d)", b)
+	}
+	return s
+}
+
+// StringToBondXmitHashPolicy returns bond lacp arte, or uknonw is the s is invalid.
+func StringToBondXmitHashPolicy(s string) BondXmitHashPolicy {
+	lacp, ok := StringToBondXmitHashPolicyMap[s]
+	if !ok {
+		return BOND_XMIT_HASH_POLICY_UNKNOWN
+	}
+	return lacp
+}
+
+// Possible BondXmitHashPolicy value
+const (
+	BOND_XMIT_HASH_POLICY_LAYER2 BondXmitHashPolicy = iota
+	BOND_XMIT_HASH_POLICY_LAYER3_4
+	BOND_XMIT_HASH_POLICY_LAYER2_3
+	BOND_XMIT_HASH_POLICY_ENCAP2_3
+	BOND_XMIT_HASH_POLICY_ENCAP3_4
+	BOND_XMIT_HASH_POLICY_UNKNOWN
+)
+
+var bondXmitHashPolicyToString = map[BondXmitHashPolicy]string{
+	BOND_XMIT_HASH_POLICY_LAYER2:   "layer2",
+	BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4",
+	BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3",
+	BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3",
+	BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4",
+}
+var StringToBondXmitHashPolicyMap = map[string]BondXmitHashPolicy{
+	"layer2":   BOND_XMIT_HASH_POLICY_LAYER2,
+	"layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4,
+	"layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3,
+	"encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3,
+	"encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4,
+}
+
+// BondLacpRate type
+type BondLacpRate int
+
+func (b BondLacpRate) String() string {
+	s, ok := bondLacpRateToString[b]
+	if !ok {
+		return fmt.Sprintf("LacpRate(%d)", b)
+	}
+	return s
+}
+
+// StringToBondLacpRate returns bond lacp arte, or uknonw is the s is invalid.
+func StringToBondLacpRate(s string) BondLacpRate {
+	lacp, ok := StringToBondLacpRateMap[s]
+	if !ok {
+		return BOND_LACP_RATE_UNKNOWN
+	}
+	return lacp
+}
+
+// Possible BondLacpRate value
+const (
+	BOND_LACP_RATE_SLOW BondLacpRate = iota
+	BOND_LACP_RATE_FAST
+	BOND_LACP_RATE_UNKNOWN
+)
+
+var bondLacpRateToString = map[BondLacpRate]string{
+	BOND_LACP_RATE_SLOW: "slow",
+	BOND_LACP_RATE_FAST: "fast",
+}
+var StringToBondLacpRateMap = map[string]BondLacpRate{
+	"slow": BOND_LACP_RATE_SLOW,
+	"fast": BOND_LACP_RATE_FAST,
+}
+
+// BondAdSelect type
+type BondAdSelect int
+
+// Possible BondAdSelect value
+const (
+	BOND_AD_SELECT_STABLE BondAdSelect = iota
+	BOND_AD_SELECT_BANDWIDTH
+	BOND_AD_SELECT_COUNT
+)
+
+// BondAdInfo
+type BondAdInfo struct {
+	AggregatorId int
+	NumPorts     int
+	ActorKey     int
+	PartnerKey   int
+	PartnerMac   net.HardwareAddr
+}
+
+// Bond representation
+type Bond struct {
+	LinkAttrs
+	Mode            BondMode
+	ActiveSlave     int
+	Miimon          int
+	UpDelay         int
+	DownDelay       int
+	UseCarrier      int
+	ArpInterval     int
+	ArpIpTargets    []net.IP
+	ArpValidate     BondArpValidate
+	ArpAllTargets   BondArpAllTargets
+	Primary         int
+	PrimaryReselect BondPrimaryReselect
+	FailOverMac     BondFailOverMac
+	XmitHashPolicy  BondXmitHashPolicy
+	ResendIgmp      int
+	NumPeerNotif    int
+	AllSlavesActive int
+	MinLinks        int
+	LpInterval      int
+	PackersPerSlave int
+	LacpRate        BondLacpRate
+	AdSelect        BondAdSelect
+	// looking at iproute tool AdInfo can only be retrived. It can't be set.
+	AdInfo *BondAdInfo
+}
+
+func NewLinkBond(atr LinkAttrs) *Bond {
+	return &Bond{
+		LinkAttrs:       atr,
+		Mode:            -1,
+		ActiveSlave:     -1,
+		Miimon:          -1,
+		UpDelay:         -1,
+		DownDelay:       -1,
+		UseCarrier:      -1,
+		ArpInterval:     -1,
+		ArpIpTargets:    nil,
+		ArpValidate:     -1,
+		ArpAllTargets:   -1,
+		Primary:         -1,
+		PrimaryReselect: -1,
+		FailOverMac:     -1,
+		XmitHashPolicy:  -1,
+		ResendIgmp:      -1,
+		NumPeerNotif:    -1,
+		AllSlavesActive: -1,
+		MinLinks:        -1,
+		LpInterval:      -1,
+		PackersPerSlave: -1,
+		LacpRate:        -1,
+		AdSelect:        -1,
+	}
+}
+
+// Flag mask for bond options. Bond.Flagmask must be set to on for option to work.
+const (
+	BOND_MODE_MASK uint64 = 1 << (1 + iota)
+	BOND_ACTIVE_SLAVE_MASK
+	BOND_MIIMON_MASK
+	BOND_UPDELAY_MASK
+	BOND_DOWNDELAY_MASK
+	BOND_USE_CARRIER_MASK
+	BOND_ARP_INTERVAL_MASK
+	BOND_ARP_VALIDATE_MASK
+	BOND_ARP_ALL_TARGETS_MASK
+	BOND_PRIMARY_MASK
+	BOND_PRIMARY_RESELECT_MASK
+	BOND_FAIL_OVER_MAC_MASK
+	BOND_XMIT_HASH_POLICY_MASK
+	BOND_RESEND_IGMP_MASK
+	BOND_NUM_PEER_NOTIF_MASK
+	BOND_ALL_SLAVES_ACTIVE_MASK
+	BOND_MIN_LINKS_MASK
+	BOND_LP_INTERVAL_MASK
+	BOND_PACKETS_PER_SLAVE_MASK
+	BOND_LACP_RATE_MASK
+	BOND_AD_SELECT_MASK
+)
+
+// Attrs implementation.
+func (bond *Bond) Attrs() *LinkAttrs {
+	return &bond.LinkAttrs
+}
+
+// Type implementation fro Vxlan.
+func (bond *Bond) Type() string {
+	return "bond"
+}
+
+// GreTap devices must specify LocalIP and RemoteIP on create
+type Gretap struct {
+	LinkAttrs
+	IKey       uint32
+	OKey       uint32
+	EncapSport uint16
+	EncapDport uint16
+	Local      net.IP
+	Remote     net.IP
+	IFlags     uint16
+	OFlags     uint16
+	PMtuDisc   uint8
+	Ttl        uint8
+	Tos        uint8
+	EncapType  uint16
+	EncapFlags uint16
+	Link       uint32
+}
+
+func (gretap *Gretap) Attrs() *LinkAttrs {
+	return &gretap.LinkAttrs
+}
+
+func (gretap *Gretap) Type() string {
+	return "gretap"
+}
+
 // iproute2 supported devices;
 // vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
 // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |

+ 330 - 0
vendor/src/github.com/vishvananda/netlink/link_linux.go

@@ -106,6 +106,24 @@ func LinkSetName(link Link, name string) error {
 	return err
 }
 
+// LinkSetAlias sets the alias of the link device.
+// Equivalent to: `ip link set dev $link alias $name`
+func LinkSetAlias(link Link, name string) error {
+	base := link.Attrs()
+	ensureIndex(base)
+	req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+	msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+	msg.Index = int32(base.Index)
+	req.AddData(msg)
+
+	data := nl.NewRtAttr(syscall.IFLA_IFALIAS, []byte(name))
+	req.AddData(data)
+
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
 // LinkSetHardwareAddr sets the hardware address of the link device.
 // Equivalent to: `ip link set $link address $hwaddr`
 func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
@@ -133,9 +151,18 @@ func LinkSetMaster(link Link, master *Bridge) error {
 		ensureIndex(masterBase)
 		index = masterBase.Index
 	}
+	if index <= 0 {
+		return fmt.Errorf("Device does not exist")
+	}
 	return LinkSetMasterByIndex(link, index)
 }
 
+// LinkSetNoMaster removes the master of the link device.
+// Equivalent to: `ip link set $link nomaster`
+func LinkSetNoMaster(link Link) error {
+	return LinkSetMasterByIndex(link, 0)
+}
+
 // LinkSetMasterByIndex sets the master of the link device.
 // Equivalent to: `ip link set $link master $master`
 func LinkSetMasterByIndex(link Link, masterIndex int) error {
@@ -275,6 +302,87 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
 	}
 }
 
+func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) {
+	data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+	if bond.Mode >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_MODE, nl.Uint8Attr(uint8(bond.Mode)))
+	}
+	if bond.ActiveSlave >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ACTIVE_SLAVE, nl.Uint32Attr(uint32(bond.ActiveSlave)))
+	}
+	if bond.Miimon >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_MIIMON, nl.Uint32Attr(uint32(bond.Miimon)))
+	}
+	if bond.UpDelay >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_UPDELAY, nl.Uint32Attr(uint32(bond.UpDelay)))
+	}
+	if bond.DownDelay >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_DOWNDELAY, nl.Uint32Attr(uint32(bond.DownDelay)))
+	}
+	if bond.UseCarrier >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_USE_CARRIER, nl.Uint8Attr(uint8(bond.UseCarrier)))
+	}
+	if bond.ArpInterval >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_INTERVAL, nl.Uint32Attr(uint32(bond.ArpInterval)))
+	}
+	if bond.ArpIpTargets != nil {
+		msg := nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_IP_TARGET, nil)
+		for i := range bond.ArpIpTargets {
+			ip := bond.ArpIpTargets[i].To4()
+			if ip != nil {
+				nl.NewRtAttrChild(msg, i, []byte(ip))
+				continue
+			}
+			ip = bond.ArpIpTargets[i].To16()
+			if ip != nil {
+				nl.NewRtAttrChild(msg, i, []byte(ip))
+			}
+		}
+	}
+	if bond.ArpValidate >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_VALIDATE, nl.Uint32Attr(uint32(bond.ArpValidate)))
+	}
+	if bond.ArpAllTargets >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_ALL_TARGETS, nl.Uint32Attr(uint32(bond.ArpAllTargets)))
+	}
+	if bond.Primary >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY, nl.Uint32Attr(uint32(bond.Primary)))
+	}
+	if bond.PrimaryReselect >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY_RESELECT, nl.Uint8Attr(uint8(bond.PrimaryReselect)))
+	}
+	if bond.FailOverMac >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_FAIL_OVER_MAC, nl.Uint8Attr(uint8(bond.FailOverMac)))
+	}
+	if bond.XmitHashPolicy >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_XMIT_HASH_POLICY, nl.Uint8Attr(uint8(bond.XmitHashPolicy)))
+	}
+	if bond.ResendIgmp >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_RESEND_IGMP, nl.Uint32Attr(uint32(bond.ResendIgmp)))
+	}
+	if bond.NumPeerNotif >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_NUM_PEER_NOTIF, nl.Uint8Attr(uint8(bond.NumPeerNotif)))
+	}
+	if bond.AllSlavesActive >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ALL_SLAVES_ACTIVE, nl.Uint8Attr(uint8(bond.AllSlavesActive)))
+	}
+	if bond.MinLinks >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_MIN_LINKS, nl.Uint32Attr(uint32(bond.MinLinks)))
+	}
+	if bond.LpInterval >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_LP_INTERVAL, nl.Uint32Attr(uint32(bond.LpInterval)))
+	}
+	if bond.PackersPerSlave >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PackersPerSlave)))
+	}
+	if bond.LacpRate >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_LACP_RATE, nl.Uint8Attr(uint8(bond.LacpRate)))
+	}
+	if bond.AdSelect >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect)))
+	}
+}
+
 // LinkAdd adds a new link device. The type and features of the device
 // are taken fromt the parameters in the link object.
 // Equivalent to: `ip link add $link`
@@ -328,6 +436,27 @@ func LinkAdd(link Link) error {
 	req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
 
 	msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+	// TODO: make it shorter
+	if base.Flags&net.FlagUp != 0 {
+		msg.Change = syscall.IFF_UP
+		msg.Flags = syscall.IFF_UP
+	}
+	if base.Flags&net.FlagBroadcast != 0 {
+		msg.Change |= syscall.IFF_BROADCAST
+		msg.Flags |= syscall.IFF_BROADCAST
+	}
+	if base.Flags&net.FlagLoopback != 0 {
+		msg.Change |= syscall.IFF_LOOPBACK
+		msg.Flags |= syscall.IFF_LOOPBACK
+	}
+	if base.Flags&net.FlagPointToPoint != 0 {
+		msg.Change |= syscall.IFF_POINTOPOINT
+		msg.Flags |= syscall.IFF_POINTOPOINT
+	}
+	if base.Flags&net.FlagMulticast != 0 {
+		msg.Change |= syscall.IFF_MULTICAST
+		msg.Flags |= syscall.IFF_MULTICAST
+	}
 	req.AddData(msg)
 
 	if base.ParentIndex != 0 {
@@ -388,6 +517,8 @@ func LinkAdd(link Link) error {
 
 	} else if vxlan, ok := link.(*Vxlan); ok {
 		addVxlanAttrs(vxlan, linkInfo)
+	} else if bond, ok := link.(*Bond); ok {
+		addBondAttrs(bond, linkInfo)
 	} else if ipv, ok := link.(*IPVlan); ok {
 		data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
 		nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
@@ -396,6 +527,8 @@ func LinkAdd(link Link) error {
 			data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
 			nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode]))
 		}
+	} else if gretap, ok := link.(*Gretap); ok {
+		addGretapAttrs(gretap, linkInfo)
 	}
 
 	req.AddData(linkInfo)
@@ -447,6 +580,20 @@ func linkByNameDump(name string) (Link, error) {
 	return nil, fmt.Errorf("Link %s not found", name)
 }
 
+func linkByAliasDump(alias string) (Link, error) {
+	links, err := LinkList()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, link := range links {
+		if link.Attrs().Alias == alias {
+			return link, nil
+		}
+	}
+	return nil, fmt.Errorf("Link alias %s not found", alias)
+}
+
 // LinkByName finds a link by name and returns a pointer to the object.
 func LinkByName(name string) (Link, error) {
 	if lookupByDump {
@@ -472,6 +619,32 @@ func LinkByName(name string) (Link, error) {
 	return link, err
 }
 
+// LinkByAlias finds a link by its alias and returns a pointer to the object.
+// If there are multiple links with the alias it returns the first one
+func LinkByAlias(alias string) (Link, error) {
+	if lookupByDump {
+		return linkByAliasDump(alias)
+	}
+
+	req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
+
+	msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+	req.AddData(msg)
+
+	nameData := nl.NewRtAttr(syscall.IFLA_IFALIAS, nl.ZeroTerminated(alias))
+	req.AddData(nameData)
+
+	link, err := execGetLink(req)
+	if err == syscall.EINVAL {
+		// older kernels don't support looking up via IFLA_IFALIAS
+		// so fall back to dumping all links
+		lookupByDump = true
+		return linkByAliasDump(alias)
+	}
+
+	return link, err
+}
+
 // LinkByIndex finds a link by index and returns a pointer to the object.
 func LinkByIndex(index int) (Link, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
@@ -543,12 +716,16 @@ func linkDeserialize(m []byte) (Link, error) {
 						link = &Veth{}
 					case "vxlan":
 						link = &Vxlan{}
+					case "bond":
+						link = &Bond{}
 					case "ipvlan":
 						link = &IPVlan{}
 					case "macvlan":
 						link = &Macvlan{}
 					case "macvtap":
 						link = &Macvtap{}
+					case "gretap":
+						link = &Gretap{}
 					default:
 						link = &GenericLink{LinkType: linkType}
 					}
@@ -562,12 +739,16 @@ func linkDeserialize(m []byte) (Link, error) {
 						parseVlanData(link, data)
 					case "vxlan":
 						parseVxlanData(link, data)
+					case "bond":
+						parseBondData(link, data)
 					case "ipvlan":
 						parseIPVlanData(link, data)
 					case "macvlan":
 						parseMacvlanData(link, data)
 					case "macvtap":
 						parseMacvtapData(link, data)
+					case "gretap":
+						parseGretapData(link, data)
 					}
 				}
 			}
@@ -591,6 +772,8 @@ func linkDeserialize(m []byte) (Link, error) {
 			base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
 		case syscall.IFLA_TXQLEN:
 			base.TxQLen = int(native.Uint32(attr.Value[0:4]))
+		case syscall.IFLA_IFALIAS:
+			base.Alias = string(attr.Value[:len(attr.Value)-1])
 		}
 	}
 	// Links that don't have IFLA_INFO_KIND are hardware devices
@@ -772,6 +955,60 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
 	}
 }
 
+func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
+	bond := NewLinkBond(NewLinkAttrs())
+	for i := range data {
+		switch data[i].Attr.Type {
+		case nl.IFLA_BOND_MODE:
+			bond.Mode = BondMode(data[i].Value[0])
+		case nl.IFLA_BOND_ACTIVE_SLAVE:
+			bond.ActiveSlave = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_MIIMON:
+			bond.Miimon = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_UPDELAY:
+			bond.UpDelay = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_DOWNDELAY:
+			bond.DownDelay = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_USE_CARRIER:
+			bond.UseCarrier = int(data[i].Value[0])
+		case nl.IFLA_BOND_ARP_INTERVAL:
+			bond.ArpInterval = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_ARP_IP_TARGET:
+			// TODO: implement
+		case nl.IFLA_BOND_ARP_VALIDATE:
+			bond.ArpValidate = BondArpValidate(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_ARP_ALL_TARGETS:
+			bond.ArpAllTargets = BondArpAllTargets(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_PRIMARY:
+			bond.Primary = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_PRIMARY_RESELECT:
+			bond.PrimaryReselect = BondPrimaryReselect(data[i].Value[0])
+		case nl.IFLA_BOND_FAIL_OVER_MAC:
+			bond.FailOverMac = BondFailOverMac(data[i].Value[0])
+		case nl.IFLA_BOND_XMIT_HASH_POLICY:
+			bond.XmitHashPolicy = BondXmitHashPolicy(data[i].Value[0])
+		case nl.IFLA_BOND_RESEND_IGMP:
+			bond.ResendIgmp = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_NUM_PEER_NOTIF:
+			bond.NumPeerNotif = int(data[i].Value[0])
+		case nl.IFLA_BOND_ALL_SLAVES_ACTIVE:
+			bond.AllSlavesActive = int(data[i].Value[0])
+		case nl.IFLA_BOND_MIN_LINKS:
+			bond.MinLinks = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_LP_INTERVAL:
+			bond.LpInterval = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_PACKETS_PER_SLAVE:
+			bond.PackersPerSlave = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_AD_LACP_RATE:
+			bond.LacpRate = BondLacpRate(data[i].Value[0])
+		case nl.IFLA_BOND_AD_SELECT:
+			bond.AdSelect = BondAdSelect(data[i].Value[0])
+		case nl.IFLA_BOND_AD_INFO:
+			// TODO: implement
+		}
+	}
+}
+
 func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
 	ipv := link.(*IPVlan)
 	for _, datum := range data {
@@ -828,3 +1065,96 @@ func linkFlags(rawFlags uint32) net.Flags {
 	}
 	return f
 }
+
+func htonl(val uint32) []byte {
+	bytes := make([]byte, 4)
+	binary.BigEndian.PutUint32(bytes, val)
+	return bytes
+}
+
+func htons(val uint16) []byte {
+	bytes := make([]byte, 2)
+	binary.BigEndian.PutUint16(bytes, val)
+	return bytes
+}
+
+func ntohl(buf []byte) uint32 {
+	return binary.BigEndian.Uint32(buf)
+}
+
+func ntohs(buf []byte) uint16 {
+	return binary.BigEndian.Uint16(buf)
+}
+
+func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
+	data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+
+	ip := gretap.Local.To4()
+	if ip != nil {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip))
+	}
+	ip = gretap.Remote.To4()
+	if ip != nil {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_REMOTE, []byte(ip))
+	}
+
+	if gretap.IKey != 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_IKEY, htonl(gretap.IKey))
+		gretap.IFlags |= uint16(nl.GRE_KEY)
+	}
+
+	if gretap.OKey != 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_OKEY, htonl(gretap.OKey))
+		gretap.OFlags |= uint16(nl.GRE_KEY)
+	}
+
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_IFLAGS, htons(gretap.IFlags))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_OFLAGS, htons(gretap.OFlags))
+
+	if gretap.Link != 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_LINK, nl.Uint32Attr(gretap.Link))
+	}
+
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(gretap.PMtuDisc))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_TTL, nl.Uint8Attr(gretap.Ttl))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_TOS, nl.Uint8Attr(gretap.Tos))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_TYPE, nl.Uint16Attr(gretap.EncapType))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_FLAGS, nl.Uint16Attr(gretap.EncapFlags))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_SPORT, htons(gretap.EncapSport))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_DPORT, htons(gretap.EncapDport))
+}
+
+func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) {
+	gre := link.(*Gretap)
+	for _, datum := range data {
+		switch datum.Attr.Type {
+		case nl.IFLA_GRE_OKEY:
+			gre.IKey = ntohl(datum.Value[0:4])
+		case nl.IFLA_GRE_IKEY:
+			gre.OKey = ntohl(datum.Value[0:4])
+		case nl.IFLA_GRE_LOCAL:
+			gre.Local = net.IP(datum.Value[0:4])
+		case nl.IFLA_GRE_REMOTE:
+			gre.Remote = net.IP(datum.Value[0:4])
+		case nl.IFLA_GRE_ENCAP_SPORT:
+			gre.EncapSport = ntohs(datum.Value[0:2])
+		case nl.IFLA_GRE_ENCAP_DPORT:
+			gre.EncapDport = ntohs(datum.Value[0:2])
+		case nl.IFLA_GRE_IFLAGS:
+			gre.IFlags = ntohs(datum.Value[0:2])
+		case nl.IFLA_GRE_OFLAGS:
+			gre.OFlags = ntohs(datum.Value[0:2])
+
+		case nl.IFLA_GRE_TTL:
+			gre.Ttl = uint8(datum.Value[0])
+		case nl.IFLA_GRE_TOS:
+			gre.Tos = uint8(datum.Value[0])
+		case nl.IFLA_GRE_PMTUDISC:
+			gre.PMtuDisc = uint8(datum.Value[0])
+		case nl.IFLA_GRE_ENCAP_TYPE:
+			gre.EncapType = native.Uint16(datum.Value[0:2])
+		case nl.IFLA_GRE_ENCAP_FLAGS:
+			gre.EncapFlags = native.Uint16(datum.Value[0:2])
+		}
+	}
+}

+ 3 - 2
vendor/src/github.com/vishvananda/netlink/neigh_linux.go

@@ -70,10 +70,10 @@ func NeighAdd(neigh *Neigh) error {
 	return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL)
 }
 
-// NeighAdd will add or replace an IP to MAC mapping to the ARP table
+// NeighSet will add or replace an IP to MAC mapping to the ARP table
 // Equivalent to: `ip neigh replace....`
 func NeighSet(neigh *Neigh) error {
-	return neighAdd(neigh, syscall.NLM_F_CREATE)
+	return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE)
 }
 
 // NeighAppend will append an entry to FDB
@@ -133,6 +133,7 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP)
 	msg := Ndmsg{
 		Family: uint8(family),
+		Index:  uint32(linkIndex),
 	}
 	req.AddData(&msg)
 

+ 5 - 2
vendor/src/github.com/vishvananda/netlink/netlink.go

@@ -33,7 +33,10 @@ func ParseIPNet(s string) (*net.IPNet, error) {
 	return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil
 }
 
-// NewIPNet generates an IPNet from an ip address using a netmask of 32.
+// NewIPNet generates an IPNet from an ip address using a netmask of 32 or 128.
 func NewIPNet(ip net.IP) *net.IPNet {
-	return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
+	if ip.To4() != nil {
+		return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
+	}
+	return &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}
 }

+ 80 - 0
vendor/src/github.com/vishvananda/netlink/nl/link_linux.go

@@ -102,3 +102,83 @@ const (
 	MACVLAN_MODE_PASSTHRU = 8
 	MACVLAN_MODE_SOURCE   = 16
 )
+
+const (
+	IFLA_BOND_UNSPEC = iota
+	IFLA_BOND_MODE
+	IFLA_BOND_ACTIVE_SLAVE
+	IFLA_BOND_MIIMON
+	IFLA_BOND_UPDELAY
+	IFLA_BOND_DOWNDELAY
+	IFLA_BOND_USE_CARRIER
+	IFLA_BOND_ARP_INTERVAL
+	IFLA_BOND_ARP_IP_TARGET
+	IFLA_BOND_ARP_VALIDATE
+	IFLA_BOND_ARP_ALL_TARGETS
+	IFLA_BOND_PRIMARY
+	IFLA_BOND_PRIMARY_RESELECT
+	IFLA_BOND_FAIL_OVER_MAC
+	IFLA_BOND_XMIT_HASH_POLICY
+	IFLA_BOND_RESEND_IGMP
+	IFLA_BOND_NUM_PEER_NOTIF
+	IFLA_BOND_ALL_SLAVES_ACTIVE
+	IFLA_BOND_MIN_LINKS
+	IFLA_BOND_LP_INTERVAL
+	IFLA_BOND_PACKETS_PER_SLAVE
+	IFLA_BOND_AD_LACP_RATE
+	IFLA_BOND_AD_SELECT
+	IFLA_BOND_AD_INFO
+)
+
+const (
+	IFLA_BOND_AD_INFO_UNSPEC = iota
+	IFLA_BOND_AD_INFO_AGGREGATOR
+	IFLA_BOND_AD_INFO_NUM_PORTS
+	IFLA_BOND_AD_INFO_ACTOR_KEY
+	IFLA_BOND_AD_INFO_PARTNER_KEY
+	IFLA_BOND_AD_INFO_PARTNER_MAC
+)
+
+const (
+	IFLA_BOND_SLAVE_UNSPEC = iota
+	IFLA_BOND_SLAVE_STATE
+	IFLA_BOND_SLAVE_MII_STATUS
+	IFLA_BOND_SLAVE_LINK_FAILURE_COUNT
+	IFLA_BOND_SLAVE_PERM_HWADDR
+	IFLA_BOND_SLAVE_QUEUE_ID
+	IFLA_BOND_SLAVE_AD_AGGREGATOR_ID
+)
+
+const (
+	IFLA_GRE_UNSPEC = iota
+	IFLA_GRE_LINK
+	IFLA_GRE_IFLAGS
+	IFLA_GRE_OFLAGS
+	IFLA_GRE_IKEY
+	IFLA_GRE_OKEY
+	IFLA_GRE_LOCAL
+	IFLA_GRE_REMOTE
+	IFLA_GRE_TTL
+	IFLA_GRE_TOS
+	IFLA_GRE_PMTUDISC
+	IFLA_GRE_ENCAP_LIMIT
+	IFLA_GRE_FLOWINFO
+	IFLA_GRE_FLAGS
+	IFLA_GRE_ENCAP_TYPE
+	IFLA_GRE_ENCAP_FLAGS
+	IFLA_GRE_ENCAP_SPORT
+	IFLA_GRE_ENCAP_DPORT
+	IFLA_GRE_COLLECT_METADATA
+	IFLA_GRE_MAX = IFLA_GRE_COLLECT_METADATA
+)
+
+const (
+	GRE_CSUM    = 0x8000
+	GRE_ROUTING = 0x4000
+	GRE_KEY     = 0x2000
+	GRE_SEQ     = 0x1000
+	GRE_STRICT  = 0x0800
+	GRE_REC     = 0x0700
+	GRE_FLAGS   = 0x00F8
+	GRE_VERSION = 0x0007
+)

+ 9 - 3
vendor/src/github.com/vishvananda/netlink/nl/nl_linux.go

@@ -149,10 +149,12 @@ func (a *RtAttr) Serialize() []byte {
 	length := a.Len()
 	buf := make([]byte, rtaAlignOf(length))
 
+	next := 4
 	if a.Data != nil {
-		copy(buf[4:], a.Data)
-	} else {
-		next := 4
+		copy(buf[next:], a.Data)
+		next += rtaAlignOf(len(a.Data))
+	}
+	if len(a.children) > 0 {
 		for _, child := range a.children {
 			childBuf := child.Serialize()
 			copy(buf[next:], childBuf)
@@ -323,6 +325,10 @@ func (s *NetlinkSocket) Close() {
 	syscall.Close(s.fd)
 }
 
+func (s *NetlinkSocket) GetFd() int {
+	return s.fd
+}
+
 func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
 	if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
 		return err

+ 37 - 0
vendor/src/github.com/vishvananda/netlink/nl/syscall.go

@@ -0,0 +1,37 @@
+package nl
+
+// syscall package lack of rule atributes type.
+// Thus there are defined below
+const (
+	FRA_UNSPEC  = iota
+	FRA_DST     /* destination address */
+	FRA_SRC     /* source address */
+	FRA_IIFNAME /* interface name */
+	FRA_GOTO    /* target to jump to (FR_ACT_GOTO) */
+	FRA_UNUSED2
+	FRA_PRIORITY /* priority/preference */
+	FRA_UNUSED3
+	FRA_UNUSED4
+	FRA_UNUSED5
+	FRA_FWMARK /* mark */
+	FRA_FLOW   /* flow/class id */
+	FRA_TUN_ID
+	FRA_SUPPRESS_IFGROUP
+	FRA_SUPPRESS_PREFIXLEN
+	FRA_TABLE  /* Extended table id */
+	FRA_FWMASK /* mask for netfilter mark */
+	FRA_OIFNAME
+)
+
+// ip rule netlink request types
+const (
+	FR_ACT_UNSPEC = iota
+	FR_ACT_TO_TBL /* Pass to fixed table */
+	FR_ACT_GOTO   /* Jump to another rule */
+	FR_ACT_NOP    /* No operation */
+	FR_ACT_RES3
+	FR_ACT_RES4
+	FR_ACT_BLACKHOLE   /* Drop without notification */
+	FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */
+	FR_ACT_PROHIBIT    /* Drop with EACCES */
+)

+ 130 - 11
vendor/src/github.com/vishvananda/netlink/nl/tc_linux.go

@@ -56,17 +56,21 @@ const (
 )
 
 const (
-	SizeofTcMsg       = 0x14
-	SizeofTcActionMsg = 0x04
-	SizeofTcPrioMap   = 0x14
-	SizeofTcRateSpec  = 0x0c
-	SizeofTcTbfQopt   = 2*SizeofTcRateSpec + 0x0c
-	SizeofTcHtbCopt   = 2*SizeofTcRateSpec + 0x14
-	SizeofTcHtbGlob   = 0x14
-	SizeofTcU32Key    = 0x10
-	SizeofTcU32Sel    = 0x10 // without keys
-	SizeofTcMirred    = 0x1c
-	SizeofTcPolice    = 2*SizeofTcRateSpec + 0x20
+	SizeofTcMsg          = 0x14
+	SizeofTcActionMsg    = 0x04
+	SizeofTcPrioMap      = 0x14
+	SizeofTcRateSpec     = 0x0c
+	SizeofTcNetemQopt    = 0x18
+	SizeofTcNetemCorr    = 0x0c
+	SizeofTcNetemReorder = 0x08
+	SizeofTcNetemCorrupt = 0x08
+	SizeofTcTbfQopt      = 2*SizeofTcRateSpec + 0x0c
+	SizeofTcHtbCopt      = 2*SizeofTcRateSpec + 0x14
+	SizeofTcHtbGlob      = 0x14
+	SizeofTcU32Key       = 0x10
+	SizeofTcU32Sel       = 0x10 // without keys
+	SizeofTcMirred       = 0x1c
+	SizeofTcPolice       = 2*SizeofTcRateSpec + 0x20
 )
 
 // struct tcmsg {
@@ -191,6 +195,121 @@ func (x *TcRateSpec) Serialize() []byte {
 	return (*(*[SizeofTcRateSpec]byte)(unsafe.Pointer(x)))[:]
 }
 
+/**
+* NETEM
+ */
+
+const (
+	TCA_NETEM_UNSPEC = iota
+	TCA_NETEM_CORR
+	TCA_NETEM_DELAY_DIST
+	TCA_NETEM_REORDER
+	TCA_NETEM_CORRUPT
+	TCA_NETEM_LOSS
+	TCA_NETEM_RATE
+	TCA_NETEM_ECN
+	TCA_NETEM_RATE64
+	TCA_NETEM_MAX = TCA_NETEM_RATE64
+)
+
+// struct tc_netem_qopt {
+//	__u32	latency;	/* added delay (us) */
+//	__u32   limit;		/* fifo limit (packets) */
+//	__u32	loss;		/* random packet loss (0=none ~0=100%) */
+//	__u32	gap;		/* re-ordering gap (0 for none) */
+//	__u32   duplicate;	/* random packet dup  (0=none ~0=100%) */
+// 	__u32	jitter;		/* random jitter in latency (us) */
+// };
+
+type TcNetemQopt struct {
+	Latency   uint32
+	Limit     uint32
+	Loss      uint32
+	Gap       uint32
+	Duplicate uint32
+	Jitter    uint32
+}
+
+func (msg *TcNetemQopt) Len() int {
+	return SizeofTcNetemQopt
+}
+
+func DeserializeTcNetemQopt(b []byte) *TcNetemQopt {
+	return (*TcNetemQopt)(unsafe.Pointer(&b[0:SizeofTcNetemQopt][0]))
+}
+
+func (x *TcNetemQopt) Serialize() []byte {
+	return (*(*[SizeofTcNetemQopt]byte)(unsafe.Pointer(x)))[:]
+}
+
+// struct tc_netem_corr {
+//  __u32   delay_corr; /* delay correlation */
+//  __u32   loss_corr;  /* packet loss correlation */
+//  __u32   dup_corr;   /* duplicate correlation  */
+// };
+
+type TcNetemCorr struct {
+	DelayCorr uint32
+	LossCorr  uint32
+	DupCorr   uint32
+}
+
+func (msg *TcNetemCorr) Len() int {
+	return SizeofTcNetemCorr
+}
+
+func DeserializeTcNetemCorr(b []byte) *TcNetemCorr {
+	return (*TcNetemCorr)(unsafe.Pointer(&b[0:SizeofTcNetemCorr][0]))
+}
+
+func (x *TcNetemCorr) Serialize() []byte {
+	return (*(*[SizeofTcNetemCorr]byte)(unsafe.Pointer(x)))[:]
+}
+
+// struct tc_netem_reorder {
+//  __u32   probability;
+//  __u32   correlation;
+// };
+
+type TcNetemReorder struct {
+	Probability uint32
+	Correlation uint32
+}
+
+func (msg *TcNetemReorder) Len() int {
+	return SizeofTcNetemReorder
+}
+
+func DeserializeTcNetemReorder(b []byte) *TcNetemReorder {
+	return (*TcNetemReorder)(unsafe.Pointer(&b[0:SizeofTcNetemReorder][0]))
+}
+
+func (x *TcNetemReorder) Serialize() []byte {
+	return (*(*[SizeofTcNetemReorder]byte)(unsafe.Pointer(x)))[:]
+}
+
+// struct tc_netem_corrupt {
+//  __u32   probability;
+//  __u32   correlation;
+// };
+
+type TcNetemCorrupt struct {
+	Probability uint32
+	Correlation uint32
+}
+
+func (msg *TcNetemCorrupt) Len() int {
+	return SizeofTcNetemCorrupt
+}
+
+func DeserializeTcNetemCorrupt(b []byte) *TcNetemCorrupt {
+	return (*TcNetemCorrupt)(unsafe.Pointer(&b[0:SizeofTcNetemCorrupt][0]))
+}
+
+func (x *TcNetemCorrupt) Serialize() []byte {
+	return (*(*[SizeofTcNetemCorrupt]byte)(unsafe.Pointer(x)))[:]
+}
+
 // struct tc_tbf_qopt {
 //   struct tc_ratespec rate;
 //   struct tc_ratespec peakrate;

+ 123 - 0
vendor/src/github.com/vishvananda/netlink/qdisc.go

@@ -2,6 +2,7 @@ package netlink
 
 import (
 	"fmt"
+	"math"
 )
 
 const (
@@ -52,6 +53,14 @@ func HandleStr(handle uint32) string {
 	}
 }
 
+func Percentage2u32(percentage float32) uint32 {
+	// FIXME this is most likely not the best way to convert from % to uint32
+	if percentage == 100 {
+		return math.MaxUint32
+	}
+	return uint32(math.MaxUint32 * (percentage / 100))
+}
+
 // PfifoFast is the default qdisc created by the kernel if one has not
 // been defined for the interface
 type PfifoFast struct {
@@ -120,6 +129,120 @@ func (qdisc *Htb) Type() string {
 	return "htb"
 }
 
+// Netem is a classless qdisc that rate limits based on tokens
+
+type NetemQdiscAttrs struct {
+	Latency       uint32  // in us
+	DelayCorr     float32 // in %
+	Limit         uint32
+	Loss          float32 // in %
+	LossCorr      float32 // in %
+	Gap           uint32
+	Duplicate     float32 // in %
+	DuplicateCorr float32 // in %
+	Jitter        uint32  // in us
+	ReorderProb   float32 // in %
+	ReorderCorr   float32 // in %
+	CorruptProb   float32 // in %
+	CorruptCorr   float32 // in %
+}
+
+func (q NetemQdiscAttrs) String() string {
+	return fmt.Sprintf(
+		"{Latency: %d, Limit: %d, Loss: %d, Gap: %d, Duplicate: %d, Jitter: %d}",
+		q.Latency, q.Limit, q.Loss, q.Gap, q.Duplicate, q.Jitter,
+	)
+}
+
+type Netem struct {
+	QdiscAttrs
+	Latency       uint32
+	DelayCorr     uint32
+	Limit         uint32
+	Loss          uint32
+	LossCorr      uint32
+	Gap           uint32
+	Duplicate     uint32
+	DuplicateCorr uint32
+	Jitter        uint32
+	ReorderProb   uint32
+	ReorderCorr   uint32
+	CorruptProb   uint32
+	CorruptCorr   uint32
+}
+
+func NewNetem(attrs QdiscAttrs, nattrs NetemQdiscAttrs) *Netem {
+	var limit uint32 = 1000
+	var loss_corr, delay_corr, duplicate_corr uint32
+	var reorder_prob, reorder_corr uint32
+	var corrupt_prob, corrupt_corr uint32
+
+	latency := nattrs.Latency
+	loss := Percentage2u32(nattrs.Loss)
+	gap := nattrs.Gap
+	duplicate := Percentage2u32(nattrs.Duplicate)
+	jitter := nattrs.Jitter
+
+	// Correlation
+	if latency > 0 && jitter > 0 {
+		delay_corr = Percentage2u32(nattrs.DelayCorr)
+	}
+	if loss > 0 {
+		loss_corr = Percentage2u32(nattrs.LossCorr)
+	}
+	if duplicate > 0 {
+		duplicate_corr = Percentage2u32(nattrs.DuplicateCorr)
+	}
+	// FIXME should validate values(like loss/duplicate are percentages...)
+	latency = time2Tick(latency)
+
+	if nattrs.Limit != 0 {
+		limit = nattrs.Limit
+	}
+	// Jitter is only value if latency is > 0
+	if latency > 0 {
+		jitter = time2Tick(jitter)
+	}
+
+	reorder_prob = Percentage2u32(nattrs.ReorderProb)
+	reorder_corr = Percentage2u32(nattrs.ReorderCorr)
+
+	if reorder_prob > 0 {
+		// ERROR if lantency == 0
+		if gap == 0 {
+			gap = 1
+		}
+	}
+
+	corrupt_prob = Percentage2u32(nattrs.CorruptProb)
+	corrupt_corr = Percentage2u32(nattrs.CorruptCorr)
+
+	return &Netem{
+		QdiscAttrs:    attrs,
+		Latency:       latency,
+		DelayCorr:     delay_corr,
+		Limit:         limit,
+		Loss:          loss,
+		LossCorr:      loss_corr,
+		Gap:           gap,
+		Duplicate:     duplicate,
+		DuplicateCorr: duplicate_corr,
+		Jitter:        jitter,
+		ReorderProb:   reorder_prob,
+		ReorderCorr:   reorder_corr,
+		CorruptProb:   corrupt_prob,
+		CorruptCorr:   corrupt_corr,
+	}
+}
+
+func (qdisc *Netem) Attrs() *QdiscAttrs {
+	return &qdisc.QdiscAttrs
+}
+
+func (qdisc *Netem) Type() string {
+	return "netem"
+}
+
 // Tbf is a classless qdisc that rate limits based on tokens
 type Tbf struct {
 	QdiscAttrs

+ 114 - 15
vendor/src/github.com/vishvananda/netlink/qdisc_linux.go

@@ -13,24 +13,37 @@ import (
 // QdiscDel will delete a qdisc from the system.
 // Equivalent to: `tc qdisc del $qdisc`
 func QdiscDel(qdisc Qdisc) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_DELQDISC, syscall.NLM_F_ACK)
-	base := qdisc.Attrs()
-	msg := &nl.TcMsg{
-		Family:  nl.FAMILY_ALL,
-		Ifindex: int32(base.LinkIndex),
-		Handle:  base.Handle,
-		Parent:  base.Parent,
-	}
-	req.AddData(msg)
+	return qdiscModify(syscall.RTM_DELQDISC, 0, qdisc)
+}
 
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+// QdiscChange will change a qdisc in place
+// Equivalent to: `tc qdisc change $qdisc`
+// The parent and handle MUST NOT be changed.
+func QdiscChange(qdisc Qdisc) error {
+	return qdiscModify(syscall.RTM_NEWQDISC, 0, qdisc)
+}
+
+// QdiscReplace will replace a qdisc to the system.
+// Equivalent to: `tc qdisc replace $qdisc`
+// The handle MUST change.
+func QdiscReplace(qdisc Qdisc) error {
+	return qdiscModify(
+		syscall.RTM_NEWQDISC,
+		syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE,
+		qdisc)
 }
 
 // QdiscAdd will add a qdisc to the system.
 // Equivalent to: `tc qdisc add $qdisc`
 func QdiscAdd(qdisc Qdisc) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_NEWQDISC, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return qdiscModify(
+		syscall.RTM_NEWQDISC,
+		syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
+		qdisc)
+}
+
+func qdiscModify(cmd, flags int, qdisc Qdisc) error {
+	req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
 	base := qdisc.Attrs()
 	msg := &nl.TcMsg{
 		Family:  nl.FAMILY_ALL,
@@ -39,6 +52,20 @@ func QdiscAdd(qdisc Qdisc) error {
 		Parent:  base.Parent,
 	}
 	req.AddData(msg)
+
+	// When deleting don't bother building the rest of the netlink payload
+	if cmd != syscall.RTM_DELQDISC {
+		if err := qdiscPayload(req, qdisc); err != nil {
+			return err
+		}
+	}
+
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
+func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
+
 	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type())))
 
 	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
@@ -65,15 +92,47 @@ func QdiscAdd(qdisc Qdisc) error {
 		opt.DirectPkts = htb.DirectPkts
 		nl.NewRtAttrChild(options, nl.TCA_HTB_INIT, opt.Serialize())
 		// nl.NewRtAttrChild(options, nl.TCA_HTB_DIRECT_QLEN, opt.Serialize())
+	} else if netem, ok := qdisc.(*Netem); ok {
+		opt := nl.TcNetemQopt{}
+		opt.Latency = netem.Latency
+		opt.Limit = netem.Limit
+		opt.Loss = netem.Loss
+		opt.Gap = netem.Gap
+		opt.Duplicate = netem.Duplicate
+		opt.Jitter = netem.Jitter
+		options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
+		// Correlation
+		corr := nl.TcNetemCorr{}
+		corr.DelayCorr = netem.DelayCorr
+		corr.LossCorr = netem.LossCorr
+		corr.DupCorr = netem.DuplicateCorr
+
+		if corr.DelayCorr > 0 || corr.LossCorr > 0 || corr.DupCorr > 0 {
+			nl.NewRtAttrChild(options, nl.TCA_NETEM_CORR, corr.Serialize())
+		}
+		// Corruption
+		corruption := nl.TcNetemCorrupt{}
+		corruption.Probability = netem.CorruptProb
+		corruption.Correlation = netem.CorruptCorr
+		if corruption.Probability > 0 {
+			nl.NewRtAttrChild(options, nl.TCA_NETEM_CORRUPT, corruption.Serialize())
+		}
+		// Reorder
+		reorder := nl.TcNetemReorder{}
+		reorder.Probability = netem.ReorderProb
+		reorder.Correlation = netem.ReorderCorr
+		if reorder.Probability > 0 {
+			nl.NewRtAttrChild(options, nl.TCA_NETEM_REORDER, reorder.Serialize())
+		}
 	} else if _, ok := qdisc.(*Ingress); ok {
 		// ingress filters must use the proper handle
-		if msg.Parent != HANDLE_INGRESS {
+		if qdisc.Attrs().Parent != HANDLE_INGRESS {
 			return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS")
 		}
 	}
+
 	req.AddData(options)
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+	return nil
 }
 
 // QdiscList gets a list of qdiscs in the system.
@@ -135,6 +194,8 @@ func QdiscList(link Link) ([]Qdisc, error) {
 					qdisc = &Ingress{}
 				case "htb":
 					qdisc = &Htb{}
+				case "netem":
+					qdisc = &Netem{}
 				default:
 					qdisc = &GenericQdisc{QdiscType: qdiscType}
 				}
@@ -166,6 +227,10 @@ func QdiscList(link Link) ([]Qdisc, error) {
 					if err := parseHtbData(qdisc, data); err != nil {
 						return nil, err
 					}
+				case "netem":
+					if err := parseNetemData(qdisc, attr.Value); err != nil {
+						return nil, err
+					}
 
 					// no options for ingress
 				}
@@ -213,6 +278,40 @@ func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
 	}
 	return nil
 }
+
+func parseNetemData(qdisc Qdisc, value []byte) error {
+	netem := qdisc.(*Netem)
+	opt := nl.DeserializeTcNetemQopt(value)
+	netem.Latency = opt.Latency
+	netem.Limit = opt.Limit
+	netem.Loss = opt.Loss
+	netem.Gap = opt.Gap
+	netem.Duplicate = opt.Duplicate
+	netem.Jitter = opt.Jitter
+	data, err := nl.ParseRouteAttr(value[nl.SizeofTcNetemQopt:])
+	if err != nil {
+		return err
+	}
+	for _, datum := range data {
+		switch datum.Attr.Type {
+		case nl.TCA_NETEM_CORR:
+			opt := nl.DeserializeTcNetemCorr(datum.Value)
+			netem.DelayCorr = opt.DelayCorr
+			netem.LossCorr = opt.LossCorr
+			netem.DuplicateCorr = opt.DupCorr
+		case nl.TCA_NETEM_CORRUPT:
+			opt := nl.DeserializeTcNetemCorrupt(datum.Value)
+			netem.CorruptProb = opt.Probability
+			netem.CorruptCorr = opt.Correlation
+		case nl.TCA_NETEM_REORDER:
+			opt := nl.DeserializeTcNetemReorder(datum.Value)
+			netem.ReorderProb = opt.Probability
+			netem.ReorderCorr = opt.Correlation
+		}
+	}
+	return nil
+}
+
 func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
 	native = nl.NativeEndian()
 	tbf := qdisc.(*Tbf)

+ 13 - 10
vendor/src/github.com/vishvananda/netlink/route.go

@@ -24,17 +24,20 @@ const (
 	FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE
 )
 
-// Route represents a netlink route. A route is associated with a link,
-// has a destination network, an optional source ip, and optional
-// gateway. Advanced route parameters and non-main routing tables are
-// currently not supported.
+// Route represents a netlink route.
 type Route struct {
-	LinkIndex int
-	Scope     Scope
-	Dst       *net.IPNet
-	Src       net.IP
-	Gw        net.IP
-	Flags     int
+	LinkIndex  int
+	ILinkIndex int
+	Scope      Scope
+	Dst        *net.IPNet
+	Src        net.IP
+	Gw         net.IP
+	Protocol   int
+	Priority   int
+	Table      int
+	Type       int
+	Tos        int
+	Flags      int
 }
 
 func (r Route) String() string {

+ 104 - 28
vendor/src/github.com/vishvananda/netlink/route_linux.go

@@ -10,6 +10,19 @@ import (
 
 // RtAttr is shared so it is in netlink_linux.go
 
+const (
+	RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
+	RT_FILTER_SCOPE
+	RT_FILTER_TYPE
+	RT_FILTER_TOS
+	RT_FILTER_IIF
+	RT_FILTER_OIF
+	RT_FILTER_DST
+	RT_FILTER_SRC
+	RT_FILTER_GW
+	RT_FILTER_TABLE
+)
+
 // RouteAdd will add a route to the system.
 // Equivalent to: `ip route add $route`
 func RouteAdd(route *Route) error {
@@ -29,8 +42,6 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
 		return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
 	}
 
-	msg.Scope = uint8(route.Scope)
-	msg.Flags = uint32(route.Flags)
 	family := -1
 	var rtAttrs []*nl.RtAttr
 
@@ -79,8 +90,34 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
 		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
 	}
 
-	msg.Family = uint8(family)
+	if route.Table > 0 {
+		if route.Table >= 256 {
+			msg.Table = syscall.RT_TABLE_UNSPEC
+			b := make([]byte, 4)
+			native.PutUint32(b, uint32(route.Table))
+			rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_TABLE, b))
+		} else {
+			msg.Table = uint8(route.Table)
+		}
+	}
+
+	if route.Priority > 0 {
+		b := make([]byte, 4)
+		native.PutUint32(b, uint32(route.Priority))
+		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PRIORITY, b))
+	}
+	if route.Tos > 0 {
+		msg.Tos = uint8(route.Tos)
+	}
+	if route.Protocol > 0 {
+		msg.Protocol = uint8(route.Protocol)
+	}
+	if route.Type > 0 {
+		msg.Type = uint8(route.Type)
+	}
 
+	msg.Scope = uint8(route.Scope)
+	msg.Family = uint8(family)
 	req.AddData(msg)
 	for _, attr := range rtAttrs {
 		req.AddData(attr)
@@ -102,61 +139,95 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
 // Equivalent to: `ip route show`.
 // The list can be filtered by link and ip family.
 func RouteList(link Link, family int) ([]Route, error) {
+	var routeFilter *Route
+	if link != nil {
+		routeFilter = &Route{
+			LinkIndex: link.Attrs().Index,
+		}
+	}
+	return RouteListFiltered(family, routeFilter, RT_FILTER_OIF)
+}
+
+// RouteListFiltered gets a list of routes in the system filtered with specified rules.
+// All rules must be defined in RouteFilter struct
+func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
-	msg := nl.NewIfInfomsg(family)
-	req.AddData(msg)
+	infmsg := nl.NewIfInfomsg(family)
+	req.AddData(infmsg)
 
 	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
 	if err != nil {
 		return nil, err
 	}
 
-	index := 0
-	if link != nil {
-		base := link.Attrs()
-		ensureIndex(base)
-		index = base.Index
-	}
-
 	var res []Route
 	for _, m := range msgs {
 		msg := nl.DeserializeRtMsg(m)
-
 		if msg.Flags&syscall.RTM_F_CLONED != 0 {
 			// Ignore cloned routes
 			continue
 		}
-
 		if msg.Table != syscall.RT_TABLE_MAIN {
-			// Ignore non-main tables
-			continue
+			if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 {
+				// Ignore non-main tables
+				continue
+			}
 		}
-
 		route, err := deserializeRoute(m)
 		if err != nil {
 			return nil, err
 		}
-
-		if link != nil && route.LinkIndex != index {
-			// Ignore routes from other interfaces
-			continue
+		if filter != nil {
+			switch {
+			case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table:
+				continue
+			case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
+				continue
+			case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope:
+				continue
+			case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type:
+				continue
+			case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
+				continue
+			case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
+				continue
+			case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
+				continue
+			case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw):
+				continue
+			case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
+				continue
+			case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil:
+				if route.Dst == nil {
+					continue
+				}
+				aMaskLen, aMaskBits := route.Dst.Mask.Size()
+				bMaskLen, bMaskBits := filter.Dst.Mask.Size()
+				if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) {
+					continue
+				}
+			}
 		}
 		res = append(res, route)
 	}
-
 	return res, nil
 }
 
 // deserializeRoute decodes a binary netlink message into a Route struct
 func deserializeRoute(m []byte) (Route, error) {
-	route := Route{}
 	msg := nl.DeserializeRtMsg(m)
 	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
 	if err != nil {
-		return route, err
+		return Route{}, err
+	}
+	route := Route{
+		Scope:    Scope(msg.Scope),
+		Protocol: int(msg.Protocol),
+		Table:    int(msg.Table),
+		Type:     int(msg.Type),
+		Tos:      int(msg.Tos),
+		Flags:    int(msg.Flags),
 	}
-	route.Scope = Scope(msg.Scope)
-	route.Flags = int(msg.Flags)
 
 	native := nl.NativeEndian()
 	for _, attr := range attrs {
@@ -171,8 +242,13 @@ func deserializeRoute(m []byte) (Route, error) {
 				Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
 			}
 		case syscall.RTA_OIF:
-			routeIndex := int(native.Uint32(attr.Value[0:4]))
-			route.LinkIndex = routeIndex
+			route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
+		case syscall.RTA_IIF:
+			route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
+		case syscall.RTA_PRIORITY:
+			route.Priority = int(native.Uint32(attr.Value[0:4]))
+		case syscall.RTA_TABLE:
+			route.Table = int(native.Uint32(attr.Value[0:4]))
 		}
 	}
 	return route, nil

+ 43 - 0
vendor/src/github.com/vishvananda/netlink/rule.go

@@ -0,0 +1,43 @@
+package netlink
+
+import (
+	"fmt"
+	"net"
+
+	"github.com/vishvananda/netlink/nl"
+)
+
+// Rule represents a netlink rule.
+type Rule struct {
+	*nl.RtMsg
+	Priority          int
+	Table             int
+	Mark              int
+	Mask              int
+	TunID             uint
+	Goto              int
+	Src               *net.IPNet
+	Dst               *net.IPNet
+	Flow              int
+	IifName           string
+	OifName           string
+	SuppressIfgroup   int
+	SuppressPrefixlen int
+}
+
+func (r Rule) String() string {
+	return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table)
+}
+
+// NewRule return empty rules.
+func NewRule() *Rule {
+	return &Rule{
+		SuppressIfgroup:   -1,
+		SuppressPrefixlen: -1,
+		Priority:          -1,
+		Mark:              -1,
+		Mask:              -1,
+		Goto:              -1,
+		Flow:              -1,
+	}
+}

+ 198 - 0
vendor/src/github.com/vishvananda/netlink/rule_linux.go

@@ -0,0 +1,198 @@
+package netlink
+
+import (
+	"fmt"
+	"net"
+	"syscall"
+
+	"github.com/vishvananda/netlink/nl"
+)
+
+// RuleAdd adds a rule to the system.
+// Equivalent to: ip rule add
+func RuleAdd(rule *Rule) error {
+	req := nl.NewNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return ruleHandle(rule, req)
+}
+
+// RuleDel deletes a rule from the system.
+// Equivalent to: ip rule del
+func RuleDel(rule *Rule) error {
+	req := nl.NewNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return ruleHandle(rule, req)
+}
+
+func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
+	msg := nl.NewRtMsg()
+	msg.Family = syscall.AF_INET
+	var dstFamily uint8
+
+	var rtAttrs []*nl.RtAttr
+	if rule.Dst != nil && rule.Dst.IP != nil {
+		dstLen, _ := rule.Dst.Mask.Size()
+		msg.Dst_len = uint8(dstLen)
+		msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP))
+		dstFamily = msg.Family
+		var dstData []byte
+		if msg.Family == syscall.AF_INET {
+			dstData = rule.Dst.IP.To4()
+		} else {
+			dstData = rule.Dst.IP.To16()
+		}
+		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
+	}
+
+	if rule.Src != nil && rule.Src.IP != nil {
+		msg.Family = uint8(nl.GetIPFamily(rule.Src.IP))
+		if dstFamily != 0 && dstFamily != msg.Family {
+			return fmt.Errorf("source and destination ip are not the same IP family")
+		}
+		srcLen, _ := rule.Src.Mask.Size()
+		msg.Src_len = uint8(srcLen)
+		var srcData []byte
+		if msg.Family == syscall.AF_INET {
+			srcData = rule.Src.IP.To4()
+		} else {
+			srcData = rule.Src.IP.To16()
+		}
+		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData))
+	}
+
+	if rule.Table >= 0 {
+		msg.Table = uint8(rule.Table)
+		if rule.Table >= 256 {
+			msg.Table = syscall.RT_TABLE_UNSPEC
+		}
+	}
+
+	req.AddData(msg)
+	for i := range rtAttrs {
+		req.AddData(rtAttrs[i])
+	}
+
+	var (
+		b      = make([]byte, 4)
+		native = nl.NativeEndian()
+	)
+
+	if rule.Priority >= 0 {
+		native.PutUint32(b, uint32(rule.Priority))
+		req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
+	}
+	if rule.Mark >= 0 {
+		native.PutUint32(b, uint32(rule.Mark))
+		req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
+	}
+	if rule.Mask >= 0 {
+		native.PutUint32(b, uint32(rule.Mask))
+		req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
+	}
+	if rule.Flow >= 0 {
+		native.PutUint32(b, uint32(rule.Flow))
+		req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
+	}
+	if rule.TunID > 0 {
+		native.PutUint32(b, uint32(rule.TunID))
+		req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
+	}
+	if rule.Table >= 256 {
+		native.PutUint32(b, uint32(rule.Table))
+		req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
+	}
+	if msg.Table > 0 {
+		if rule.SuppressPrefixlen >= 0 {
+			native.PutUint32(b, uint32(rule.SuppressPrefixlen))
+			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
+		}
+		if rule.SuppressIfgroup >= 0 {
+			native.PutUint32(b, uint32(rule.SuppressIfgroup))
+			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b))
+		}
+	}
+	if rule.IifName != "" {
+		req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName)))
+	}
+	if rule.OifName != "" {
+		req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName)))
+	}
+	if rule.Goto >= 0 {
+		msg.Type = nl.FR_ACT_NOP
+		native.PutUint32(b, uint32(rule.Goto))
+		req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
+	}
+
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
+// RuleList lists rules in the system.
+// Equivalent to: ip rule list
+func RuleList(family int) ([]Rule, error) {
+	req := nl.NewNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST)
+	msg := nl.NewIfInfomsg(family)
+	req.AddData(msg)
+
+	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWRULE)
+	if err != nil {
+		return nil, err
+	}
+
+	native := nl.NativeEndian()
+	var res = make([]Rule, 0)
+	for i := range msgs {
+		msg := nl.DeserializeRtMsg(msgs[i])
+		attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():])
+		if err != nil {
+			return nil, err
+		}
+
+		rule := NewRule()
+		rule.RtMsg = msg
+
+		for j := range attrs {
+			switch attrs[j].Attr.Type {
+			case syscall.RTA_TABLE:
+				rule.Table = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_SRC:
+				rule.Src = &net.IPNet{
+					IP:   attrs[j].Value,
+					Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)),
+				}
+			case nl.FRA_DST:
+				rule.Dst = &net.IPNet{
+					IP:   attrs[j].Value,
+					Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)),
+				}
+			case nl.FRA_FWMARK:
+				rule.Mark = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_FWMASK:
+				rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_TUN_ID:
+				rule.TunID = uint(native.Uint64(attrs[j].Value[0:4]))
+			case nl.FRA_IIFNAME:
+				rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
+			case nl.FRA_OIFNAME:
+				rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
+			case nl.FRA_SUPPRESS_PREFIXLEN:
+				i := native.Uint32(attrs[j].Value[0:4])
+				if i != 0xffffffff {
+					rule.SuppressPrefixlen = int(i)
+				}
+			case nl.FRA_SUPPRESS_IFGROUP:
+				i := native.Uint32(attrs[j].Value[0:4])
+				if i != 0xffffffff {
+					rule.SuppressIfgroup = int(i)
+				}
+			case nl.FRA_FLOW:
+				rule.Flow = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_GOTO:
+				rule.Goto = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_PRIORITY:
+				rule.Priority = int(native.Uint32(attrs[j].Value[0:4]))
+			}
+		}
+		res = append(res, *rule)
+	}
+
+	return res, nil
+}