diff --git a/cmd/ensdns/main.go b/cmd/ensdns/main.go
new file mode 100644
index 0000000000..88f41294a4
--- /dev/null
+++ b/cmd/ensdns/main.go
@@ -0,0 +1,189 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "encoding/hex"
+ "errors"
+ "flag"
+ "log"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/contracts/ens/contract"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethclient"
+ "github.com/miekg/dns"
+)
+
+const NotAuthoritative = errors.New("No resolver for this domain")
+const NoSuchName = errors.New("Name not found")
+
+var (
+ ethapiFlag = flag.String("ethapi", "ws://localhost:8546", "Path to connect to Ethereum node on")
+ registryAddressFlag = flag.String("registry", "", "Address of ENS registry")
+ listenAddressFlag = flag.String("address", ":53", "Address and port to listen on")
+)
+
+func nameHash(name string) common.Hash {
+ if name == "" {
+ return common.Hash{}
+ }
+
+ parts := strings.SplitN(name, ".", 2)
+ label := crypto.Keccak256Hash([]byte(parts[0]))
+ parent := common.Hash{}
+ if len(parts) > 1 {
+ parent = nameHash(parts[1])
+ }
+ return crypto.Keccak256Hash(parent[:], label[:])
+}
+
+type ENSDNS struct {
+ backend bind.ContractBackend
+ ens *contract.ENSSession
+}
+
+func New(backend bind.ContractBackend, registryAddress common.Address) (*ENSDNS, error) {
+ ens, err := contract.NewENS(registryAddress, backend)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ENSDNS{
+ backend: backend,
+ ens: &contract.ENSSession{
+ Contract: ens,
+ TransactOpts: bind.TransactOpts{},
+ },
+ }, nil
+}
+
+func (ed *ENSDNS) Handle(w dns.ResponseWriter, r *dns.Msg) {
+ m := new(dns.Msg)
+ m.SetReply(r)
+
+ for _, question := range r.Question {
+ answers, err := ed.resolve(question)
+ if err != nil {
+ log.Printf("Error resolving query %v: %v", question, err)
+ m.Rcode = dns.RcodeServerFailure
+ break
+ }
+
+ // If no answer is found, and this wasn't a CNAME or * query, try looking for CNAMEs
+ if len(answers) == 0 && question.Qtype != dns.TypeCNAME && question.Qtype != dns.TypeANY {
+ question.Qtype = dns.TypeCNAME
+ answers, err = ed.resolve(question)
+ if err != nil {
+ log.Printf("Error resolving query %v: %v", question, err)
+ m.Rcode = dns.RcodeServerFailure
+ break
+ }
+ }
+
+ m.Answer = append(m.Answer, answers...)
+ m.Authoritative = true
+ }
+
+ w.WriteMsg(m)
+}
+
+func (ed *ENSDNS) getResolver(node common.Hash) (*contract.ResolverSession, error) {
+ resolverAddr, err := ed.ens.Resolver(node)
+ if err != nil {
+ return nil, err
+ }
+
+ resolver, err := contract.NewResolver(resolverAddr, ed.backend)
+ if err != nil {
+ return nil, err
+ }
+
+ return &contract.ResolverSession{
+ Contract: resolver,
+ TransactOpts: ed.ens.TransactOpts,
+ }, nil
+}
+
+func (ed *ENSDNS) resolve(question dns.Question) (records []dns.RR, err error) {
+ log.Printf("Resolving query: %s", question.String())
+
+ node := nameHash(question.Name)
+
+ resolver, err := ed.getResolver(node)
+ if err != nil {
+ return nil, err
+ }
+
+ ttl, err := ed.ens.Ttl(node)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := 0; ; i++ {
+ response, err := resolver.Dnsrr(node, question.Qtype, question.Qclass, uint32(i))
+ if err != nil {
+ return nil, err
+ } else if response.Rtype == 0 {
+ break
+ }
+
+ hexdata := hex.EncodeToString(response.Data)
+
+ records = append(records, &dns.RFC3597{
+ Hdr: dns.RR_Header{
+ Name: question.Name,
+ Rrtype: response.Rtype,
+ Class: response.Rclass,
+ Ttl: uint32(ttl),
+ },
+ Rdata: hexdata,
+ })
+ }
+
+ return records, nil
+}
+
+func serve(addr string) {
+ server := &dns.Server{Addr: addr, Net: "udp", TsigSecret: nil}
+ if err := server.ListenAndServe(); err != nil {
+ log.Fatalf("DNS server failed: %v", err)
+ }
+}
+
+func main() {
+ flag.Parse()
+
+ client, err := ethclient.Dial(*ethapiFlag)
+ if err != nil {
+ log.Fatalf("Error connecting to Ethereum API: %v", err)
+ }
+
+ ensdns, err := New(client, common.HexToAddress(*registryAddressFlag))
+ if err != nil {
+ log.Fatalf("Error constructing ENSDNS: %v", err)
+ }
+
+ dns.HandleFunc(".", ensdns.Handle)
+ go serve(*listenAddressFlag)
+
+ log.Printf("Listening on %s", *listenAddressFlag)
+
+ select {}
+}
diff --git a/contracts/ens/contract/ens.go b/contracts/ens/contract/ens.go
index aca16de508..803060636c 100644
--- a/contracts/ens/contract/ens.go
+++ b/contracts/ens/contract/ens.go
@@ -13,18 +13,18 @@ import (
)
// ENSABI is the input ABI used to generate the binding from.
-const ENSABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"type":"function"},{"inputs":[{"name":"owner","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"}]`
+const ENSABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"resolver\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"label\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setSubnodeOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"setTTL\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"ttl\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"setResolver\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"label\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"NewOwner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"NewResolver\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"NewTTL\",\"type\":\"event\"}]"
// ENSBin is the compiled bytecode used for deploying new contracts.
-const ENSBin = `0x606060405260405160208061032683395060806040525160008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a03191682179055506102c88061005e6000396000f3606060405260e060020a60003504630178b8bf811461004757806302571be31461006e57806306ab5923146100915780631896f70a146100c85780635b0fc9c3146100fc575b005b610130600435600081815260208190526040902060010154600160a060020a03165b919050565b610130600435600081815260208190526040902054600160a060020a0316610069565b6100456004356024356044356000838152602081905260408120548490600160a060020a0390811633919091161461014d57610002565b6100456004356024356000828152602081905260409020548290600160a060020a039081163391909116146101e757610002565b6100456004356024356000828152602081905260409020548290600160a060020a0390811633919091161461025957610002565b60408051600160a060020a03929092168252519081900360200190f35b60408051868152602081810187905282519182900383018220600160a060020a03871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a382600060005060008460001916815260200190815260200160002060005060000160006101000a815481600160a060020a03021916908302179055505050505050565b60408051600160a060020a0384168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a2506000828152602081905260409020600101805473ffffffffffffffffffffffffffffffffffffffff1916821790555050565b60408051600160a060020a0384168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a2506000828152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff191682179055505056`
+const ENSBin = `0x606060405260008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb580546c0100000000000000000000000033810204600160a060020a031990911617905561044b8061005e6000396000f3606060405236156100615760e060020a60003504630178b8bf811461006657806302571be31461009257806306ab5923146100ba57806314ab9038146100f657806316a25cbd1461012f5780631896f70a146101635780635b0fc9c31461019c575b610002565b34610002576101d5600435600081815260208190526040902060010154600160a060020a03165b919050565b34610002576101d5600435600081815260208190526040902054600160a060020a031661008d565b34610002576101f16004356024356044356000838152602081905260408120548490600160a060020a0390811633919091161461021057610002565b34610002576101f16004356024356000828152602081905260409020548290600160a060020a039081163391909116146102b357610002565b34610002576101f360043560008181526020819052604090206001015467ffffffffffffffff60a060020a9091041661008d565b34610002576101f16004356024356000828152602081905260409020548290600160a060020a0390811633919091161461035657610002565b34610002576101f16004356024356000828152602081905260409020548290600160a060020a039081163391909116146103d257610002565b60408051600160a060020a039092168252519081900360200190f35b005b6040805167ffffffffffffffff9092168252519081900360200190f35b60408051868152602080820187905282519182900383018220600160a060020a03871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a382600060005060008460001916815260200190815260200160002060005060000160006101000a815481600160a060020a030219169083606060020a9081020402179055505050505050565b6040805167ffffffffffffffff84168152905184917f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68919081900360200190a26000838152602081905260409020600101805478010000000000000000000000000000000000000000000000008085020460a060020a027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909116179055505050565b60408051600160a060020a0384168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a260008381526020819052604090206001018054606060020a8085020473ffffffffffffffffffffffffffffffffffffffff19909116179055505050565b60408051600160a060020a0384168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a260008381526020819052604090208054606060020a8085020473ffffffffffffffffffffffffffffffffffffffff1990911617905550505056`
// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it.
-func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend, owner common.Address) (common.Address, *types.Transaction, *ENS, error) {
+func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ENS, error) {
parsed, err := abi.JSON(strings.NewReader(ENSABI))
if err != nil {
return common.Address{}, nil, nil, err
}
- address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend, owner)
+ address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
@@ -210,6 +210,32 @@ func (_ENS *ENSCallerSession) Resolver(node [32]byte) (common.Address, error) {
return _ENS.Contract.Resolver(&_ENS.CallOpts, node)
}
+// Ttl is a free data retrieval call binding the contract method 0x16a25cbd.
+//
+// Solidity: function ttl(node bytes32) constant returns(uint64)
+func (_ENS *ENSCaller) Ttl(opts *bind.CallOpts, node [32]byte) (uint64, error) {
+ var (
+ ret0 = new(uint64)
+ )
+ out := ret0
+ err := _ENS.contract.Call(opts, out, "ttl", node)
+ return *ret0, err
+}
+
+// Ttl is a free data retrieval call binding the contract method 0x16a25cbd.
+//
+// Solidity: function ttl(node bytes32) constant returns(uint64)
+func (_ENS *ENSSession) Ttl(node [32]byte) (uint64, error) {
+ return _ENS.Contract.Ttl(&_ENS.CallOpts, node)
+}
+
+// Ttl is a free data retrieval call binding the contract method 0x16a25cbd.
+//
+// Solidity: function ttl(node bytes32) constant returns(uint64)
+func (_ENS *ENSCallerSession) Ttl(node [32]byte) (uint64, error) {
+ return _ENS.Contract.Ttl(&_ENS.CallOpts, node)
+}
+
// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3.
//
// Solidity: function setOwner(node bytes32, owner address) returns()
@@ -273,11 +299,32 @@ func (_ENS *ENSTransactorSession) SetSubnodeOwner(node [32]byte, label [32]byte,
return _ENS.Contract.SetSubnodeOwner(&_ENS.TransactOpts, node, label, owner)
}
+// SetTTL is a paid mutator transaction binding the contract method 0x14ab9038.
+//
+// Solidity: function setTTL(node bytes32, ttl uint64) returns()
+func (_ENS *ENSTransactor) SetTTL(opts *bind.TransactOpts, node [32]byte, ttl uint64) (*types.Transaction, error) {
+ return _ENS.contract.Transact(opts, "setTTL", node, ttl)
+}
+
+// SetTTL is a paid mutator transaction binding the contract method 0x14ab9038.
+//
+// Solidity: function setTTL(node bytes32, ttl uint64) returns()
+func (_ENS *ENSSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, error) {
+ return _ENS.Contract.SetTTL(&_ENS.TransactOpts, node, ttl)
+}
+
+// SetTTL is a paid mutator transaction binding the contract method 0x14ab9038.
+//
+// Solidity: function setTTL(node bytes32, ttl uint64) returns()
+func (_ENS *ENSTransactorSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, error) {
+ return _ENS.Contract.SetTTL(&_ENS.TransactOpts, node, ttl)
+}
+
// FIFSRegistrarABI is the input ABI used to generate the binding from.
-const FIFSRegistrarABI = `[{"constant":false,"inputs":[{"name":"subnode","type":"bytes32"},{"name":"owner","type":"address"}],"name":"register","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"},{"name":"node","type":"bytes32"}],"type":"constructor"}]`
+const FIFSRegistrarABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"subnode\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"},{\"name\":\"node\",\"type\":\"bytes32\"}],\"type\":\"constructor\"}]"
// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts.
-const FIFSRegistrarBin = `0x6060604081815280610620833960a090525160805160008054600160a060020a031916831790558160a0610367806100878339018082600160a060020a03168152602001915050604051809103906000f0600160006101000a815481600160a060020a0302191690830217905550806002600050819055505050610232806103ee6000396000f3606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056606060405260e060020a6000350463d22057a9811461001b575b005b61001960043560243560025460408051918252602082810185905260008054835194859003840185207f02571be300000000000000000000000000000000000000000000000000000000865260048601819052935193949193600160a060020a03909116926302571be39260248181019391829003018187876161da5a03f11561000257505060405151915050600160a060020a0381166000148015906100d4575033600160a060020a031681600160a060020a031614155b156100de57610002565b60408051600080546002547f06ab592300000000000000000000000000000000000000000000000000000000845260048401526024830188905230600160a060020a03908116604485015293519316926306ab5923926064818101939291829003018183876161da5a03f11561000257505060008054600154604080517f1896f70a00000000000000000000000000000000000000000000000000000000815260048101889052600160a060020a0392831660248201529051929091169350631896f70a926044828101939192829003018183876161da5a03f11561000257505060008054604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260048101879052600160a060020a0388811660248301529151929091169350635b0fc9c3926044828101939192829003018183876161da5a03f115610002575050505050505056`
+const FIFSRegistrarBin = `0x60606040818152806106aa833960a0905251608051600080546c0100000000000000000000000080850204600160a060020a031990911617905560405182906103aa806100a58339018082600160a060020a03168152602001915050604051809103906000f0801560025760018054600160a060020a0319166c01000000000000000000000000928302929092049190911790556002555061025b8061044f6000396000f360606040526040516020806103aa833950608060405251600080546c0100000000000000000000000080840204600160a060020a0319909116179055506103608061004a6000396000f3606060405236156100565760e060020a600035046301ffc9a781146100635780632dff6941146100d75780633b3b57de1461010057806341b9dc2b1461013c578063c3d014d6146101da578063d5fa2b0014610267575b34610002576102f4610002565b34610002576102f660043560007f3b3b57de00000000000000000000000000000000000000000000000000000000600160e060020a0319831614806100d157507fd8389dc500000000000000000000000000000000000000000000000000000000600160e060020a03198316145b92915050565b346100025760043560009081526002602052604090205460408051918252519081900360200190f35b3461000257600435600090815260016020526040902054600160a060020a031660408051600160a060020a039092168252519081900360200190f35b34610002576102f660043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156101915750600083815260016020526040902054600160a060020a031615155b806101d357507f6861736800000000000000000000000000000000000000000000000000000000821480156101d3575060008381526002602052604090205415155b9392505050565b34610002576102f460043560243560008054604080516020908101849052815160e060020a6302571be30281526004810187905291518694600160a060020a033381169516936302571be393602480830194919391928390030190829087803b156100025760325a03f11561000257505060405151600160a060020a031691909114905061030a57610002565b34610002576102f460043560243560008054604080516020908101849052815160e060020a6302571be30281526004810187905291518694600160a060020a033381169516936302571be393602480830194919391928390030190829087803b156100025760325a03f11561000257505060405151600160a060020a031691909114905061031d57610002565b005b604080519115158252519081900360200190f35b5060009182526002602052604090912055565b600083815260016020526040902080546c010000000000000000000000008085020473ffffffffffffffffffffffffffffffffffffffff1990911617905550505056606060405260e060020a6000350463d22057a9811461001e575b610002565b34610002576100f1600435602435600254604080519182526020808301859052815192839003820183206000805494830181905283517f02571be300000000000000000000000000000000000000000000000000000000815260048101839052935191949093600160a060020a03909116926302571be39260248084019382900301818787803b156100025760325a03f11561000257505060405151915050600160a060020a038116158015906100e7575033600160a060020a031681600160a060020a031614155b156100f357610002565b005b60008054600254604080517f06ab5923000000000000000000000000000000000000000000000000000000008152600481019290925260248201889052600160a060020a03308116604484015290519216926306ab59239260648084019382900301818387803b156100025760325a03f11561000257505060008054600154604080517f1896f70a00000000000000000000000000000000000000000000000000000000815260048101889052600160a060020a0392831660248201529051919092169350631896f70a9260448084019391929182900301818387803b156100025760325a03f11561000257505060008054604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260048101879052600160a060020a0388811660248301529151919092169350635b0fc9c39260448084019391929182900301818387803b156100025760325a03f115610002575050505050505056`
// DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it.
func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) {
@@ -441,10 +488,10 @@ func (_FIFSRegistrar *FIFSRegistrarTransactorSession) Register(subnode [32]byte,
}
// PublicResolverABI is the input ABI used to generate the binding from.
-const PublicResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]`
+const PublicResolverABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"interfaceID\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"content\",\"outputs\":[{\"name\":\"ret\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"addr\",\"outputs\":[{\"name\":\"ret\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"kind\",\"type\":\"bytes32\"}],\"name\":\"has\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"setContent\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"setAddr\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"}]"
// PublicResolverBin is the compiled bytecode used for deploying new contracts.
-const PublicResolverBin = `0x606060405260405160208061036783395060806040525160008054600160a060020a0319168217905550610330806100376000396000f36060604052361561004b5760e060020a60003504632dff694181146100535780633b3b57de1461007557806341b9dc2b146100a0578063c3d014d614610139578063d5fa2b00146101b2575b61022b610002565b61022d6004356000818152600260205260408120549081141561027057610002565b61023f600435600081815260016020526040812054600160a060020a03169081141561027057610002565b61025c60043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156100f05750600083815260016020526040812054600160a060020a031614155b8061013257507f636f6e74656e740000000000000000000000000000000000000000000000000082148015610132575060008381526002602052604081205414155b9392505050565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a031691909114905061027557610002565b61022b600435602435600080546040805160e060020a6302571be30281526004810186905290518593600160a060020a033381169416926302571be392602482810193602093839003909101908290876161da5a03f11561000257505060405151600160a060020a03169190911490506102c157610002565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b604080519115158252519081900360200190f35b919050565b6000838152600260209081526040918290208490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600083815260016020908152604091829020805473ffffffffffffffffffffffffffffffffffffffff1916851790558151600160a060020a0385168152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a250505056`
+const PublicResolverBin = `0x60606040526040516020806103aa833950608060405251600080546c0100000000000000000000000080840204600160a060020a0319909116179055506103608061004a6000396000f3606060405236156100565760e060020a600035046301ffc9a781146100635780632dff6941146100d75780633b3b57de1461010057806341b9dc2b1461013c578063c3d014d6146101da578063d5fa2b0014610267575b34610002576102f4610002565b34610002576102f660043560007f3b3b57de00000000000000000000000000000000000000000000000000000000600160e060020a0319831614806100d157507fd8389dc500000000000000000000000000000000000000000000000000000000600160e060020a03198316145b92915050565b346100025760043560009081526002602052604090205460408051918252519081900360200190f35b3461000257600435600090815260016020526040902054600160a060020a031660408051600160a060020a039092168252519081900360200190f35b34610002576102f660043560243560007f6164647200000000000000000000000000000000000000000000000000000000821480156101915750600083815260016020526040902054600160a060020a031615155b806101d357507f6861736800000000000000000000000000000000000000000000000000000000821480156101d3575060008381526002602052604090205415155b9392505050565b34610002576102f460043560243560008054604080516020908101849052815160e060020a6302571be30281526004810187905291518694600160a060020a033381169516936302571be393602480830194919391928390030190829087803b156100025760325a03f11561000257505060405151600160a060020a031691909114905061030a57610002565b34610002576102f460043560243560008054604080516020908101849052815160e060020a6302571be30281526004810187905291518694600160a060020a033381169516936302571be393602480830194919391928390030190829087803b156100025760325a03f11561000257505060405151600160a060020a031691909114905061031d57610002565b005b604080519115158252519081900360200190f35b5060009182526002602052604090912055565b600083815260016020526040902080546c010000000000000000000000008085020473ffffffffffffffffffffffffffffffffffffffff1990911617905550505056`
// DeployPublicResolver deploys a new Ethereum contract, binding an instance of PublicResolver to it.
func DeployPublicResolver(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address) (common.Address, *types.Transaction, *PublicResolver, error) {
@@ -638,25 +685,56 @@ func (_PublicResolver *PublicResolverCallerSession) Content(node [32]byte) ([32]
return _PublicResolver.Contract.Content(&_PublicResolver.CallOpts, node)
}
-// Has is a paid mutator transaction binding the contract method 0x41b9dc2b.
+// Has is a free data retrieval call binding the contract method 0x41b9dc2b.
//
-// Solidity: function has(node bytes32, kind bytes32) returns(bool)
-func (_PublicResolver *PublicResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) {
- return _PublicResolver.contract.Transact(opts, "has", node, kind)
+// Solidity: function has(node bytes32, kind bytes32) constant returns(bool)
+func (_PublicResolver *PublicResolverCaller) Has(opts *bind.CallOpts, node [32]byte, kind [32]byte) (bool, error) {
+ var (
+ ret0 = new(bool)
+ )
+ out := ret0
+ err := _PublicResolver.contract.Call(opts, out, "has", node, kind)
+ return *ret0, err
}
-// Has is a paid mutator transaction binding the contract method 0x41b9dc2b.
+// Has is a free data retrieval call binding the contract method 0x41b9dc2b.
//
-// Solidity: function has(node bytes32, kind bytes32) returns(bool)
-func (_PublicResolver *PublicResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) {
- return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind)
+// Solidity: function has(node bytes32, kind bytes32) constant returns(bool)
+func (_PublicResolver *PublicResolverSession) Has(node [32]byte, kind [32]byte) (bool, error) {
+ return _PublicResolver.Contract.Has(&_PublicResolver.CallOpts, node, kind)
}
-// Has is a paid mutator transaction binding the contract method 0x41b9dc2b.
+// Has is a free data retrieval call binding the contract method 0x41b9dc2b.
//
-// Solidity: function has(node bytes32, kind bytes32) returns(bool)
-func (_PublicResolver *PublicResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) {
- return _PublicResolver.Contract.Has(&_PublicResolver.TransactOpts, node, kind)
+// Solidity: function has(node bytes32, kind bytes32) constant returns(bool)
+func (_PublicResolver *PublicResolverCallerSession) Has(node [32]byte, kind [32]byte) (bool, error) {
+ return _PublicResolver.Contract.Has(&_PublicResolver.CallOpts, node, kind)
+}
+
+// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7.
+//
+// Solidity: function supportsInterface(interfaceID bytes4) constant returns(bool)
+func (_PublicResolver *PublicResolverCaller) SupportsInterface(opts *bind.CallOpts, interfaceID [4]byte) (bool, error) {
+ var (
+ ret0 = new(bool)
+ )
+ out := ret0
+ err := _PublicResolver.contract.Call(opts, out, "supportsInterface", interfaceID)
+ return *ret0, err
+}
+
+// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7.
+//
+// Solidity: function supportsInterface(interfaceID bytes4) constant returns(bool)
+func (_PublicResolver *PublicResolverSession) SupportsInterface(interfaceID [4]byte) (bool, error) {
+ return _PublicResolver.Contract.SupportsInterface(&_PublicResolver.CallOpts, interfaceID)
+}
+
+// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7.
+//
+// Solidity: function supportsInterface(interfaceID bytes4) constant returns(bool)
+func (_PublicResolver *PublicResolverCallerSession) SupportsInterface(interfaceID [4]byte) (bool, error) {
+ return _PublicResolver.Contract.SupportsInterface(&_PublicResolver.CallOpts, interfaceID)
}
// SetAddr is a paid mutator transaction binding the contract method 0xd5fa2b00.
@@ -702,7 +780,7 @@ func (_PublicResolver *PublicResolverTransactorSession) SetContent(node [32]byte
}
// ResolverABI is the input ABI used to generate the binding from.
-const ResolverABI = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"kind","type":"bytes32"}],"name":"has","outputs":[{"name":"","type":"bool"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"}]`
+const ResolverABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"interfaceID\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"content\",\"outputs\":[{\"name\":\"ret\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"addr\",\"outputs\":[{\"name\":\"ret\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"qtype\",\"type\":\"uint16\"},{\"name\":\"qclass\",\"type\":\"uint16\"},{\"name\":\"index\",\"type\":\"uint32\"}],\"name\":\"dnsrr\",\"outputs\":[{\"name\":\"rtype\",\"type\":\"uint16\"},{\"name\":\"rclass\",\"type\":\"uint16\"},{\"name\":\"data\",\"type\":\"bytes\"}],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"a\",\"type\":\"address\"}],\"name\":\"AddrChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"ContentChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"qtype\",\"type\":\"uint16\"},{\"indexed\":false,\"name\":\"qclass\",\"type\":\"uint16\"},{\"indexed\":false,\"name\":\"index\",\"type\":\"uint32\"}],\"name\":\"DnsrrChanged\",\"type\":\"event\"}]"
// ResolverBin is the compiled bytecode used for deploying new contracts.
const ResolverBin = `0x`
@@ -899,23 +977,68 @@ func (_Resolver *ResolverCallerSession) Content(node [32]byte) ([32]byte, error)
return _Resolver.Contract.Content(&_Resolver.CallOpts, node)
}
-// Has is a paid mutator transaction binding the contract method 0x41b9dc2b.
+// Dnsrr is a free data retrieval call binding the contract method 0xfcca1f4e.
//
-// Solidity: function has(node bytes32, kind bytes32) returns(bool)
-func (_Resolver *ResolverTransactor) Has(opts *bind.TransactOpts, node [32]byte, kind [32]byte) (*types.Transaction, error) {
- return _Resolver.contract.Transact(opts, "has", node, kind)
+// Solidity: function dnsrr(node bytes32, qtype uint16, qclass uint16, index uint32) constant returns(rtype uint16, rclass uint16, data bytes)
+func (_Resolver *ResolverCaller) Dnsrr(opts *bind.CallOpts, node [32]byte, qtype uint16, qclass uint16, index uint32) (struct {
+ Rtype uint16
+ Rclass uint16
+ Data []byte
+}, error) {
+ ret := new(struct {
+ Rtype uint16
+ Rclass uint16
+ Data []byte
+ })
+ out := ret
+ err := _Resolver.contract.Call(opts, out, "dnsrr", node, qtype, qclass, index)
+ return *ret, err
}
-// Has is a paid mutator transaction binding the contract method 0x41b9dc2b.
+// Dnsrr is a free data retrieval call binding the contract method 0xfcca1f4e.
//
-// Solidity: function has(node bytes32, kind bytes32) returns(bool)
-func (_Resolver *ResolverSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) {
- return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind)
+// Solidity: function dnsrr(node bytes32, qtype uint16, qclass uint16, index uint32) constant returns(rtype uint16, rclass uint16, data bytes)
+func (_Resolver *ResolverSession) Dnsrr(node [32]byte, qtype uint16, qclass uint16, index uint32) (struct {
+ Rtype uint16
+ Rclass uint16
+ Data []byte
+}, error) {
+ return _Resolver.Contract.Dnsrr(&_Resolver.CallOpts, node, qtype, qclass, index)
}
-// Has is a paid mutator transaction binding the contract method 0x41b9dc2b.
+// Dnsrr is a free data retrieval call binding the contract method 0xfcca1f4e.
//
-// Solidity: function has(node bytes32, kind bytes32) returns(bool)
-func (_Resolver *ResolverTransactorSession) Has(node [32]byte, kind [32]byte) (*types.Transaction, error) {
- return _Resolver.Contract.Has(&_Resolver.TransactOpts, node, kind)
+// Solidity: function dnsrr(node bytes32, qtype uint16, qclass uint16, index uint32) constant returns(rtype uint16, rclass uint16, data bytes)
+func (_Resolver *ResolverCallerSession) Dnsrr(node [32]byte, qtype uint16, qclass uint16, index uint32) (struct {
+ Rtype uint16
+ Rclass uint16
+ Data []byte
+}, error) {
+ return _Resolver.Contract.Dnsrr(&_Resolver.CallOpts, node, qtype, qclass, index)
+}
+
+// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7.
+//
+// Solidity: function supportsInterface(interfaceID bytes4) constant returns(bool)
+func (_Resolver *ResolverCaller) SupportsInterface(opts *bind.CallOpts, interfaceID [4]byte) (bool, error) {
+ var (
+ ret0 = new(bool)
+ )
+ out := ret0
+ err := _Resolver.contract.Call(opts, out, "supportsInterface", interfaceID)
+ return *ret0, err
+}
+
+// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7.
+//
+// Solidity: function supportsInterface(interfaceID bytes4) constant returns(bool)
+func (_Resolver *ResolverSession) SupportsInterface(interfaceID [4]byte) (bool, error) {
+ return _Resolver.Contract.SupportsInterface(&_Resolver.CallOpts, interfaceID)
+}
+
+// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7.
+//
+// Solidity: function supportsInterface(interfaceID bytes4) constant returns(bool)
+func (_Resolver *ResolverCallerSession) SupportsInterface(interfaceID [4]byte) (bool, error) {
+ return _Resolver.Contract.SupportsInterface(&_Resolver.CallOpts, interfaceID)
}
diff --git a/contracts/ens/contract/ens.sol b/contracts/ens/contract/ens.sol
index 114cd7319f..ad302584ca 100644
--- a/contracts/ens/contract/ens.sol
+++ b/contracts/ens/contract/ens.sol
@@ -14,6 +14,7 @@ contract ENS {
struct Record {
address owner;
address resolver;
+ uint64 ttl;
}
mapping(bytes32=>Record) records;
@@ -24,20 +25,23 @@ contract ENS {
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
- // Logged when the owner of a node changes the resolver for that node.
+ // Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
+
+ // Logged when the TTL of a node changes
+ event NewTTL(bytes32 indexed node, uint64 ttl);
// Permits modifications only by the owner of the specified node.
modifier only_owner(bytes32 node) {
if(records[node].owner != msg.sender) throw;
- _
+ _;
}
/**
- * Constructs a new ENS registrar, with the provided address as the owner of the root node.
+ * Constructs a new ENS registrar.
*/
- function ENS(address owner) {
- records[0].owner = owner;
+ function ENS() {
+ records[0].owner = msg.sender;
}
/**
@@ -54,6 +58,13 @@ contract ENS {
return records[node].resolver;
}
+ /**
+ * Returns the TTL of a node, and any records associated with it.
+ */
+ function ttl(bytes32 node) constant returns (uint64) {
+ return records[node].ttl;
+ }
+
/**
* Transfers ownership of a node to a new address. May only be called by the current
* owner of the node.
@@ -87,6 +98,28 @@ contract ENS {
NewResolver(node, resolver);
records[node].resolver = resolver;
}
+
+ /**
+ * Sets the TTL for the specified node.
+ * @param node The node to update.
+ * @param ttl The TTL in seconds.
+ */
+ function setTTL(bytes32 node, uint64 ttl) only_owner(node) {
+ NewTTL(node, ttl);
+ records[node].ttl = ttl;
+ }
+}
+
+contract Resolver {
+ event AddrChanged(bytes32 indexed node, address a);
+ event ContentChanged(bytes32 indexed node, bytes32 hash);
+ event DnsrrChanged(bytes32 indexed node, uint16 qtype, uint16 qclass, uint32 index);
+
+ function supportsInterface(bytes4 interfaceID) constant returns (bool);
+ function addr(bytes32 node) constant returns (address ret);
+ function content(bytes32 node) constant returns (bytes32 ret);
+ function dnsrr(bytes32 node, uint16 qtype, uint16 qclass, uint32 index)
+ constant returns (uint16 rtype, uint16 rclass, bytes data);
}
/**
@@ -130,27 +163,18 @@ contract FIFSRegistrar {
}
}
-contract Resolver {
- event AddrChanged(bytes32 indexed node, address a);
- event ContentChanged(bytes32 indexed node, bytes32 hash);
-
- function has(bytes32 node, bytes32 kind) returns (bool);
- function addr(bytes32 node) constant returns (address ret);
- function content(bytes32 node) constant returns (bytes32 ret);
-}
-
/**
* A simple resolver anyone can use; only allows the owner of a node to set its
* address.
*/
-contract PublicResolver is Resolver {
+contract PublicResolver {
ENS ens;
mapping(bytes32=>address) addresses;
- mapping(bytes32=>bytes32) contents;
-
+ mapping(bytes32=>bytes32) hashes;
+
modifier only_owner(bytes32 node) {
if(ens.owner(node) != msg.sender) throw;
- _
+ _;
}
/**
@@ -175,9 +199,17 @@ contract PublicResolver is Resolver {
* @return True if this resolver has a record of the provided type on the
* provided node.
*/
- function has(bytes32 node, bytes32 kind) returns (bool) {
- return (kind == "addr" && addresses[node] != 0) ||
- (kind == "content" && contents[node] != 0);
+ function has(bytes32 node, bytes32 kind) constant returns (bool) {
+ return (kind == "addr" && addresses[node] != 0) || (kind == "hash" && hashes[node] != 0);
+ }
+
+ /**
+ * Returns true if the resolver implements the interface specified by the provided hash.
+ * @param interfaceID The ID of the interface to check for.
+ * @return True if the contract implements the requested interface.
+ */
+ function supportsInterface(bytes4 interfaceID) constant returns (bool) {
+ return interfaceID == 0x3b3b57de || interfaceID == 0xd8389dc5;
}
/**
@@ -187,19 +219,6 @@ contract PublicResolver is Resolver {
*/
function addr(bytes32 node) constant returns (address ret) {
ret = addresses[node];
- if(ret == 0)
- throw;
- }
-
- /**
- * Returns the content hash associated with an ENS node.
- * @param node The ENS node to query.
- * @return The associated content hash.
- */
- function content(bytes32 node) constant returns (bytes32 ret) {
- ret = contents[node];
- if(ret == 0)
- throw;
}
/**
@@ -210,17 +229,28 @@ contract PublicResolver is Resolver {
*/
function setAddr(bytes32 node, address addr) only_owner(node) {
addresses[node] = addr;
- AddrChanged(node, addr);
+ }
+
+ /**
+ * Returns the content hash associated with an ENS node.
+ * Note that this resource type is not standardized, and will likely change
+ * in future to a resource type based on multihash.
+ * @param node The ENS node to query.
+ * @return The associated content hash.
+ */
+ function content(bytes32 node) constant returns (bytes32 ret) {
+ ret = hashes[node];
}
/**
* Sets the content hash associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
+ * Note that this resource type is not standardized, and will likely change
+ * in future to a resource type based on multihash.
* @param node The node to update.
- * @param hash The content hash to set.
+ * @param hash The content hash to set
*/
function setContent(bytes32 node, bytes32 hash) only_owner(node) {
- contents[node] = hash;
- ContentChanged(node, hash);
+ hashes[node] = hash;
}
}
diff --git a/contracts/ens/ens.go b/contracts/ens/ens.go
index 7806742cf7..2c8082f08d 100644
--- a/contracts/ens/ens.go
+++ b/contracts/ens/ens.go
@@ -55,7 +55,7 @@ func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contra
// DeployENS deploys an instance of the ENS nameservice, with a 'first in first served' root registrar.
func DeployENS(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (*ENS, error) {
// Deploy the ENS registry
- ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend, transactOpts.From)
+ ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend)
if err != nil {
return nil, err
}
diff --git a/vendor.conf b/vendor.conf
index 1428a1b590..8b2bcd0900 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -17,6 +17,7 @@ github.com/maruel/panicparse ad66119
github.com/mattn/go-colorable v0.0.6-9-gd228849
github.com/mattn/go-isatty 30a891c
github.com/mattn/go-runewidth v0.0.1-10-g737072b
+github.com/miekg/dns 4f8d08a
github.com/mitchellh/go-wordwrap ad45545
github.com/nsf/termbox-go abe82ce
github.com/pborman/uuid v1.0-19-g5007efa
diff --git a/vendor/github.com/miekg/dns/.gitignore b/vendor/github.com/miekg/dns/.gitignore
new file mode 100644
index 0000000000..776cd950c2
--- /dev/null
+++ b/vendor/github.com/miekg/dns/.gitignore
@@ -0,0 +1,4 @@
+*.6
+tags
+test.out
+a.out
diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml
new file mode 100644
index 0000000000..1f056ab7cc
--- /dev/null
+++ b/vendor/github.com/miekg/dns/.travis.yml
@@ -0,0 +1,7 @@
+language: go
+sudo: false
+go:
+ - 1.5
+ - 1.6
+script:
+ - go test -race -v -bench=.
diff --git a/vendor/github.com/miekg/dns/AUTHORS b/vendor/github.com/miekg/dns/AUTHORS
new file mode 100644
index 0000000000..1965683525
--- /dev/null
+++ b/vendor/github.com/miekg/dns/AUTHORS
@@ -0,0 +1 @@
+Miek Gieben
diff --git a/vendor/github.com/miekg/dns/CONTRIBUTORS b/vendor/github.com/miekg/dns/CONTRIBUTORS
new file mode 100644
index 0000000000..f77e8a895f
--- /dev/null
+++ b/vendor/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
diff --git a/vendor/github.com/miekg/dns/COPYRIGHT b/vendor/github.com/miekg/dns/COPYRIGHT
new file mode 100644
index 0000000000..35702b10e8
--- /dev/null
+++ b/vendor/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.
diff --git a/vendor/github.com/miekg/dns/LICENSE b/vendor/github.com/miekg/dns/LICENSE
new file mode 100644
index 0000000000..5763fa7fe5
--- /dev/null
+++ b/vendor/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.
+
diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md
new file mode 100644
index 0000000000..0e3356cb90
--- /dev/null
+++ b/vendor/github.com/miekg/dns/README.md
@@ -0,0 +1,154 @@
+[](https://travis-ci.org/miekg/dns) [](https://godoc.org/github.com/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.5 and 1.6.
+
+# 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/hashicorp/consul
+* https://github.com/DevelopersPL/godnsagent
+* https://github.com/duedil-ltd/discodns
+* https://github.com/StalkR/dns-reverse-proxy
+* https://github.com/tianon/rawdns
+* https://mesosphere.github.io/mesos-dns/
+* https://pulse.turbobytes.com/
+* https://play.google.com/store/apps/details?id=com.turbobytes.dig
+* https://github.com/fcambus/statzone
+* https://github.com/benschw/dns-clb-go
+* https://github.com/corny/dnscheck for http://public-dns.info/
+* https://namesmith.io
+* https://github.com/miekg/unbound
+* https://github.com/miekg/exdns
+* https://dnslookup.org
+* https://github.com/looterz/grimd
+* https://github.com/phamhongviet/serf-dns
+* https://github.com/mehrdadrad/mylg
+* https://github.com/bamarni/dockness
+* https://github.com/fffaraz/microdns
+
+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, Cookies;
+* AXFR/IXFR;
+* TSIG, SIG(0);
+* DNS over TLS: optional encrypted connection between client and server;
+* DNS name compression;
+* Depends only on the standard library.
+
+Have fun!
+
+Miek Gieben - 2010-2012 -
+
+# Building
+
+Building is done with the `go` tool. If you have setup your GOPATH
+correctly, the following should work:
+
+ go get github.com/miekg/dns
+ go build github.com/miekg/dns
+
+## Examples
+
+A short "how to use the API" is at the beginning of doc.go (this also will show
+when you call `godoc github.com/miekg/dns`).
+
+Example programs can be found in the `github.com/miekg/exdns` repository.
+
+## Supported RFCs
+
+*all of them*
+
+* 103{4,5} - DNS standard
+* 1348 - NSAP record (removed the record)
+* 1982 - Serial Arithmetic
+* 1876 - LOC record
+* 1995 - IXFR
+* 1996 - DNS notify
+* 2136 - DNS Update (dynamic updates)
+* 2181 - RRset definition - there is no RRset type though, just []RR
+* 2537 - RSAMD5 DNS keys
+* 2065 - DNSSEC (updated in later RFCs)
+* 2671 - EDNS record
+* 2782 - SRV record
+* 2845 - TSIG record
+* 2915 - NAPTR record
+* 2929 - DNS IANA Considerations
+* 3110 - RSASHA1 DNS keys
+* 3225 - DO bit (DNSSEC OK)
+* 340{1,2,3} - NAPTR record
+* 3445 - Limiting the scope of (DNS)KEY
+* 3597 - Unknown RRs
+* 403{3,4,5} - DNSSEC + validation functions
+* 4255 - SSHFP record
+* 4343 - Case insensitivity
+* 4408 - SPF record
+* 4509 - SHA256 Hash in DS
+* 4592 - Wildcards in the DNS
+* 4635 - HMAC SHA TSIG
+* 4701 - DHCID
+* 4892 - id.server
+* 5001 - NSID
+* 5155 - NSEC3 record
+* 5205 - HIP record
+* 5702 - SHA2 in the DNS
+* 5936 - AXFR
+* 5966 - TCP implementation recommendations
+* 6605 - ECDSA
+* 6725 - IANA Registry Update
+* 6742 - ILNP DNS
+* 6840 - Clarifications and Implementation Notes for DNS Security
+* 6844 - CAA record
+* 6891 - EDNS0 update
+* 6895 - DNS IANA considerations
+* 6975 - Algorithm Understanding in DNSSEC
+* 7043 - EUI48/EUI64 records
+* 7314 - DNS (EDNS) EXPIRE Option
+* 7553 - URI record
+* 7858 - DNS over TLS: Initiation and Performance Considerations (draft)
+* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
+* xxxx - EDNS0 DNS Update Lease (draft)
+
+## Loosely based upon
+
+* `ldns`
+* `NSD`
+* `Net::DNS`
+* `GRONG`
diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go
new file mode 100644
index 0000000000..0db7f7bf68
--- /dev/null
+++ b/vendor/github.com/miekg/dns/client.go
@@ -0,0 +1,455 @@
+package dns
+
+// A client implementation.
+
+import (
+ "bytes"
+ "crypto/tls"
+ "encoding/binary"
+ "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 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" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
+ UDPSize uint16 // minimum receive buffer for UDP messages
+ TLSConfig *tls.Config // TLS connection configuration
+ Timeout time.Duration // a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout and WriteTimeout when non-zero
+ DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - overridden by Timeout when that value is non-zero
+ ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
+ WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
+ TsigSecret map[string]string // secret(s) for Tsig map[], zonename 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 a reply. Exchange does not retry a failed query, nor
+// will it fall back to TCP in case of truncation.
+// See client.Exchange for more information on setting larger buffer sizes.
+func Exchange(m *Msg, a string) (r *Msg, err error) {
+ 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 a synchronous query. It sends the message m to the address
+// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
+//
+// c := new(dns.Client)
+// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
+//
+// Exchange does not retry a failed query, nor will it fall back to TCP in
+// case of truncation.
+// It is up to the caller to create a message that allows for larger responses to be
+// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
+// buffer, see SetEdns0. Messsages without an OPT RR will fallback to the historic limit
+// of 512 bytes.
+func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+ if !c.SingleInflight {
+ return c.exchange(m, a)
+ }
+ // 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.Timeout != 0 {
+ return c.Timeout
+ }
+ if c.DialTimeout != 0 {
+ return c.DialTimeout
+ }
+ return dnsTimeout
+}
+
+func (c *Client) readTimeout() time.Duration {
+ if c.ReadTimeout != 0 {
+ return c.ReadTimeout
+ }
+ return dnsTimeout
+}
+
+func (c *Client) writeTimeout() time.Duration {
+ if c.WriteTimeout != 0 {
+ return c.WriteTimeout
+ }
+ return dnsTimeout
+}
+
+func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
+ var co *Conn
+ network := "udp"
+ tls := false
+
+ switch c.Net {
+ case "tcp-tls":
+ network = "tcp"
+ tls = true
+ case "tcp4-tls":
+ network = "tcp4"
+ tls = true
+ case "tcp6-tls":
+ network = "tcp6"
+ tls = true
+ default:
+ if c.Net != "" {
+ network = c.Net
+ }
+ }
+
+ var deadline time.Time
+ if c.Timeout != 0 {
+ deadline = time.Now().Add(c.Timeout)
+ }
+
+ if tls {
+ co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout())
+ } else {
+ co, err = DialTimeout(network, 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(deadlineOrTimeout(deadline, c.writeTimeout()))
+ if err = co.WriteMsg(m); err != nil {
+ return nil, 0, err
+ }
+
+ co.SetReadDeadline(deadlineOrTimeout(deadline, 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
+ )
+
+ switch t := co.Conn.(type) {
+ case *net.TCPConn, *tls.Conn:
+ r := t.(io.Reader)
+
+ // First two bytes specify the length of the entire message.
+ l, err := tcpMsgLen(r)
+ if err != nil {
+ return nil, err
+ }
+ p = make([]byte, l)
+ n, err = tcpRead(r, p)
+ co.rtt = time.Since(co.t)
+ default:
+ if co.UDPSize > MinMsgSize {
+ p = make([]byte, co.UDPSize)
+ } else {
+ p = make([]byte, MinMsgSize)
+ }
+ n, err = co.Read(p)
+ co.rtt = time.Since(co.t)
+ }
+
+ if err != nil {
+ return nil, err
+ } else if n < headerSize {
+ return nil, ErrShortRead
+ }
+
+ p = p[:n]
+ if hdr != nil {
+ dh, _, err := unpackMsgHdr(p, 0)
+ if err != nil {
+ return nil, err
+ }
+ *hdr = dh
+ }
+ return p, err
+}
+
+// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
+func tcpMsgLen(t io.Reader) (int, error) {
+ p := []byte{0, 0}
+ n, err := t.Read(p)
+ if err != nil {
+ return 0, err
+ }
+ if n != 2 {
+ return 0, ErrShortRead
+ }
+ l := binary.BigEndian.Uint16(p)
+ if l == 0 {
+ return 0, ErrShortRead
+ }
+ return int(l), nil
+}
+
+// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
+func tcpRead(t io.Reader, p []byte) (int, error) {
+ n, err := t.Read(p)
+ if err != nil {
+ return n, err
+ }
+ for n < len(p) {
+ j, err := t.Read(p[n:])
+ if err != nil {
+ return n, err
+ }
+ n += j
+ }
+ return n, err
+}
+
+// Read implements the net.Conn read method.
+func (co *Conn) Read(p []byte) (n int, err error) {
+ if co.Conn == nil {
+ return 0, ErrConnEmpty
+ }
+ if len(p) < 2 {
+ return 0, io.ErrShortBuffer
+ }
+ switch t := co.Conn.(type) {
+ case *net.TCPConn, *tls.Conn:
+ r := t.(io.Reader)
+
+ l, err := tcpMsgLen(r)
+ if err != nil {
+ return 0, err
+ }
+ if l > len(p) {
+ return int(l), io.ErrShortBuffer
+ }
+ return tcpRead(r, p[:l])
+ }
+ // UDP connection
+ n, err = co.Conn.Read(p)
+ if err != nil {
+ return n, err
+ }
+ return n, err
+}
+
+// WriteMsg sends a message through the connection co.
+// If the message m contains a TSIG record the transaction
+// signature is calculated.
+func (co *Conn) WriteMsg(m *Msg) (err error) {
+ var out []byte
+ if t := m.IsTsig(); t != nil {
+ mac := ""
+ if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
+ return ErrSecret
+ }
+ out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
+ // Set for the next read, although only used in zone transfers
+ co.tsigRequestMAC = mac
+ } else {
+ out, err = m.Pack()
+ }
+ if err != nil {
+ return err
+ }
+ 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) {
+ switch t := co.Conn.(type) {
+ case *net.TCPConn, *tls.Conn:
+ w := t.(io.Writer)
+
+ lp := len(p)
+ if lp < 2 {
+ return 0, io.ErrShortBuffer
+ }
+ if lp > MaxMsgSize {
+ return 0, &Error{err: "message too large"}
+ }
+ l := make([]byte, 2, lp+2)
+ binary.BigEndian.PutUint16(l, uint16(lp))
+ p = append(l, p...)
+ n, err := io.Copy(w, bytes.NewReader(p))
+ return int(n), err
+ }
+ 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
+}
+
+// DialWithTLS connects to the address on the named network with TLS.
+func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) {
+ conn = new(Conn)
+ conn.Conn, err = tls.Dial(network, address, tlsConfig)
+ if err != nil {
+ return nil, err
+ }
+ return conn, nil
+}
+
+// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
+func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) {
+ var dialer net.Dialer
+ dialer.Timeout = timeout
+
+ conn = new(Conn)
+ conn.Conn, err = tls.DialWithDialer(&dialer, network, address, tlsConfig)
+ if err != nil {
+ return nil, err
+ }
+ return conn, nil
+}
+
+func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time {
+ if deadline.IsZero() {
+ return time.Now().Add(timeout)
+ }
+ return deadline
+}
diff --git a/vendor/github.com/miekg/dns/client_test.go b/vendor/github.com/miekg/dns/client_test.go
new file mode 100644
index 0000000000..850bcfcda6
--- /dev/null
+++ b/vendor/github.com/miekg/dns/client_test.go
@@ -0,0 +1,452 @@
+package dns
+
+import (
+ "crypto/tls"
+ "fmt"
+ "net"
+ "strconv"
+ "testing"
+ "time"
+)
+
+func TestClientSync(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ if r != nil && r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+ // And now with plain Exchange().
+ r, err = Exchange(m, addrstr)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ if r == nil || r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+}
+
+func TestClientTLSSync(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ cert, err := tls.X509KeyPair(CertPEMBlock, KeyPEMBlock)
+ if err != nil {
+ t.Fatalf("unable to build certificate: %v", err)
+ }
+
+ config := tls.Config{
+ Certificates: []tls.Certificate{cert},
+ }
+
+ s, addrstr, err := RunLocalTLSServer("127.0.0.1:0", &config)
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ c.Net = "tcp-tls"
+ c.TLSConfig = &tls.Config{
+ InsecureSkipVerify: true,
+ }
+
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ if r != nil && r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+}
+
+func TestClientSyncBadId(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServerBadId)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ c := new(Client)
+ if _, _, err := c.Exchange(m, addrstr); err != ErrId {
+ t.Errorf("did not find a bad Id")
+ }
+ // And now with plain Exchange().
+ if _, err := Exchange(m, addrstr); err != ErrId {
+ t.Errorf("did not find a bad Id")
+ }
+}
+
+func TestClientEDNS0(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeDNSKEY)
+
+ m.SetEdns0(2048, true)
+
+ c := new(Client)
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+
+ if r != nil && r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+}
+
+// Validates the transmission and parsing of local EDNS0 options.
+func TestClientEDNS0Local(t *testing.T) {
+ optStr1 := "1979:0x0707"
+ optStr2 := strconv.Itoa(EDNS0LOCALSTART) + ":0x0601"
+
+ handler := func(w ResponseWriter, req *Msg) {
+ m := new(Msg)
+ m.SetReply(req)
+
+ m.Extra = make([]RR, 1, 2)
+ m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello local edns"}}
+
+ // If the local options are what we expect, then reflect them back.
+ ec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String()
+ ec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String()
+ if ec1 == optStr1 && ec2 == optStr2 {
+ m.Extra = append(m.Extra, req.Extra[0])
+ }
+
+ w.WriteMsg(m)
+ }
+
+ HandleFunc("miek.nl.", handler)
+ defer HandleRemove("miek.nl.")
+
+ s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %s", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeTXT)
+
+ // Add two local edns options to the query.
+ ec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}}
+ ec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}}
+ o := &OPT{Hdr: RR_Header{Name: ".", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}}
+ m.Extra = append(m.Extra, o)
+
+ c := new(Client)
+ r, _, err := c.Exchange(m, addrstr)
+ if err != nil {
+ t.Errorf("failed to exchange: %s", err)
+ }
+
+ if r != nil && r.Rcode != RcodeSuccess {
+ t.Error("failed to get a valid answer")
+ t.Logf("%v\n", r)
+ }
+
+ txt := r.Extra[0].(*TXT).Txt[0]
+ if txt != "Hello local edns" {
+ t.Error("Unexpected result for miek.nl", txt, "!= Hello local edns")
+ }
+
+ // Validate the local options in the reply.
+ got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String()
+ if got != optStr1 {
+ t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr1)
+ t.Logf("%v\n", r)
+ }
+
+ got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String()
+ if got != optStr2 {
+ t.Errorf("failed to get local edns0 answer; got %s, expected %s", got, optStr2)
+ t.Logf("%v\n", r)
+ }
+}
+
+// ExampleTsigSecret_updateLeaseTSIG shows how to update a lease signed with TSIG
+func ExampleTsigSecret_updateLeaseTSIG() {
+ m := new(Msg)
+ m.SetUpdate("t.local.ip6.io.")
+ rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
+ rrs := make([]RR, 1)
+ rrs[0] = rr
+ m.Insert(rrs)
+
+ leaseRr := new(OPT)
+ leaseRr.Hdr.Name = "."
+ leaseRr.Hdr.Rrtype = TypeOPT
+ e := new(EDNS0_UL)
+ e.Code = EDNS0UL
+ e.Lease = 120
+ leaseRr.Option = append(leaseRr.Option, e)
+ m.Extra = append(m.Extra, leaseRr)
+
+ c := new(Client)
+ m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix())
+ c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="}
+
+ _, _, err := c.Exchange(m, "127.0.0.1:53")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func TestClientConn(t *testing.T) {
+ HandleFunc("miek.nl.", HelloServer)
+ defer HandleRemove("miek.nl.")
+
+ // This uses TCP just to make it slightly different than TestClientSync
+ s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer s.Shutdown()
+
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSOA)
+
+ cn, err := Dial("tcp", addrstr)
+ if err != nil {
+ t.Errorf("failed to dial %s: %v", addrstr, err)
+ }
+
+ err = cn.WriteMsg(m)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ r, err := cn.ReadMsg()
+ if r == nil || r.Rcode != RcodeSuccess {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+
+ err = cn.WriteMsg(m)
+ if err != nil {
+ t.Errorf("failed to exchange: %v", err)
+ }
+ h := new(Header)
+ buf, err := cn.ReadMsgHeader(h)
+ if buf == nil {
+ t.Errorf("failed to get an valid answer\n%v", r)
+ }
+ if int(h.Bits&0xF) != RcodeSuccess {
+ t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
+ }
+ if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
+ t.Errorf("expected to have question and additional in response; got something else: %+v", h)
+ }
+ if err = r.Unpack(buf); err != nil {
+ t.Errorf("unable to unpack message fully: %v", err)
+ }
+}
+
+func TestTruncatedMsg(t *testing.T) {
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeSRV)
+ cnt := 10
+ for i := 0; i < cnt; i++ {
+ r := &SRV{
+ Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeSRV, Class: ClassINET, Ttl: 0},
+ Port: uint16(i + 8000),
+ Target: "target.miek.nl.",
+ }
+ m.Answer = append(m.Answer, r)
+
+ re := &A{
+ Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeA, Class: ClassINET, Ttl: 0},
+ A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i)).To4(),
+ }
+ m.Extra = append(m.Extra, re)
+ }
+ buf, err := m.Pack()
+ if err != nil {
+ t.Errorf("failed to pack: %v", err)
+ }
+
+ r := new(Msg)
+ if err = r.Unpack(buf); err != nil {
+ t.Errorf("unable to unpack message: %v", err)
+ }
+ if len(r.Answer) != cnt {
+ t.Errorf("answer count after regular unpack doesn't match: %d", len(r.Answer))
+ }
+ if len(r.Extra) != cnt {
+ t.Errorf("extra count after regular unpack doesn't match: %d", len(r.Extra))
+ }
+
+ m.Truncated = true
+ buf, err = m.Pack()
+ if err != nil {
+ t.Errorf("failed to pack truncated: %v", err)
+ }
+
+ r = new(Msg)
+ if err = r.Unpack(buf); err != nil && err != ErrTruncated {
+ t.Errorf("unable to unpack truncated message: %v", err)
+ }
+ if !r.Truncated {
+ t.Errorf("truncated message wasn't unpacked as truncated")
+ }
+ if len(r.Answer) != cnt {
+ t.Errorf("answer count after truncated unpack doesn't match: %d", len(r.Answer))
+ }
+ if len(r.Extra) != cnt {
+ t.Errorf("extra count after truncated unpack doesn't match: %d", len(r.Extra))
+ }
+
+ // Now we want to remove almost all of the extra records
+ // We're going to loop over the extra to get the count of the size of all
+ // of them
+ off := 0
+ buf1 := make([]byte, m.Len())
+ for i := 0; i < len(m.Extra); i++ {
+ off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
+ if err != nil {
+ t.Errorf("failed to pack extra: %v", err)
+ }
+ }
+
+ // Remove all of the extra bytes but 10 bytes from the end of buf
+ off -= 10
+ buf1 = buf[:len(buf)-off]
+
+ r = new(Msg)
+ if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
+ t.Errorf("unable to unpack cutoff message: %v", err)
+ }
+ if !r.Truncated {
+ t.Error("truncated cutoff message wasn't unpacked as truncated")
+ }
+ if len(r.Answer) != cnt {
+ t.Errorf("answer count after cutoff unpack doesn't match: %d", len(r.Answer))
+ }
+ if len(r.Extra) != 0 {
+ t.Errorf("extra count after cutoff unpack is not zero: %d", len(r.Extra))
+ }
+
+ // Now we want to remove almost all of the answer records too
+ buf1 = make([]byte, m.Len())
+ as := 0
+ for i := 0; i < len(m.Extra); i++ {
+ off1 := off
+ off, err = PackRR(m.Extra[i], buf1, off, nil, m.Compress)
+ as = off - off1
+ if err != nil {
+ t.Errorf("failed to pack extra: %v", err)
+ }
+ }
+
+ // Keep exactly one answer left
+ // This should still cause Answer to be nil
+ off -= as
+ buf1 = buf[:len(buf)-off]
+
+ r = new(Msg)
+ if err = r.Unpack(buf1); err != nil && err != ErrTruncated {
+ t.Errorf("unable to unpack cutoff message: %v", err)
+ }
+ if !r.Truncated {
+ t.Error("truncated cutoff message wasn't unpacked as truncated")
+ }
+ if len(r.Answer) != 0 {
+ t.Errorf("answer count after second cutoff unpack is not zero: %d", len(r.Answer))
+ }
+
+ // Now leave only 1 byte of the question
+ // Since the header is always 12 bytes, we just need to keep 13
+ buf1 = buf[:13]
+
+ r = new(Msg)
+ err = r.Unpack(buf1)
+ if err == nil || err == ErrTruncated {
+ t.Errorf("error should not be ErrTruncated from question cutoff unpack: %v", err)
+ }
+
+ // Finally, if we only have the header, we should still return an error
+ buf1 = buf[:12]
+
+ r = new(Msg)
+ if err = r.Unpack(buf1); err == nil || err != ErrTruncated {
+ t.Errorf("error not ErrTruncated from header-only unpack: %v", err)
+ }
+}
+
+func TestTimeout(t *testing.T) {
+ // Set up a dummy UDP server that won't respond
+ addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unable to resolve local udp address: %v", err)
+ }
+ conn, err := net.ListenUDP("udp", addr)
+ if err != nil {
+ t.Fatalf("unable to run test server: %v", err)
+ }
+ defer conn.Close()
+ addrstr := conn.LocalAddr().String()
+
+ // Message to send
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeTXT)
+
+ // Use a channel + timeout to ensure we don't get stuck if the
+ // Client Timeout is not working properly
+ done := make(chan struct{})
+
+ timeout := time.Millisecond
+ allowable := timeout + (10 * time.Millisecond)
+ abortAfter := timeout + (100 * time.Millisecond)
+
+ start := time.Now()
+
+ go func() {
+ c := &Client{Timeout: timeout}
+ _, _, err := c.Exchange(m, addrstr)
+ if err == nil {
+ t.Error("no timeout using Client")
+ }
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ case <-time.After(abortAfter):
+ }
+
+ length := time.Since(start)
+
+ if length > allowable {
+ t.Errorf("exchange took longer (%v) than specified Timeout (%v)", length, timeout)
+ }
+}
diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go
new file mode 100644
index 0000000000..cfa9ad0b22
--- /dev/null
+++ b/vendor/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
+}
diff --git a/vendor/github.com/miekg/dns/clientconfig_test.go b/vendor/github.com/miekg/dns/clientconfig_test.go
new file mode 100644
index 0000000000..63bc5c814b
--- /dev/null
+++ b/vendor/github.com/miekg/dns/clientconfig_test.go
@@ -0,0 +1,50 @@
+package dns
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+const normal string = `
+# Comment
+domain somedomain.com
+nameserver 10.28.10.2
+nameserver 11.28.10.1
+`
+
+const missingNewline string = `
+domain somedomain.com
+nameserver 10.28.10.2
+nameserver 11.28.10.1` // <- NOTE: NO newline.
+
+func testConfig(t *testing.T, data string) {
+ tempDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("tempDir: %v", err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ path := filepath.Join(tempDir, "resolv.conf")
+ if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
+ t.Fatalf("writeFile: %v", err)
+ }
+ cc, err := ClientConfigFromFile(path)
+ if err != nil {
+ t.Errorf("error parsing resolv.conf: %v", err)
+ }
+ if l := len(cc.Servers); l != 2 {
+ t.Errorf("incorrect number of nameservers detected: %d", l)
+ }
+ if l := len(cc.Search); l != 1 {
+ t.Errorf("domain directive not parsed correctly: %v", cc.Search)
+ } else {
+ if cc.Search[0] != "somedomain.com" {
+ t.Errorf("domain is unexpected: %v", cc.Search[0])
+ }
+ }
+}
+
+func TestNameserver(t *testing.T) { testConfig(t, normal) }
+func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }
diff --git a/vendor/github.com/miekg/dns/dane.go b/vendor/github.com/miekg/dns/dane.go
new file mode 100644
index 0000000000..cdaa833ff2
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dane.go
@@ -0,0 +1,44 @@
+package dns
+
+import (
+ "crypto/sha256"
+ "crypto/sha512"
+ "crypto/x509"
+ "encoding/hex"
+ "errors"
+ "io"
+)
+
+// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records.
+func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) {
+ switch matchingType {
+ case 0:
+ switch selector {
+ case 0:
+ return hex.EncodeToString(cert.Raw), nil
+ case 1:
+ return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil
+ }
+ case 1:
+ h := sha256.New()
+ switch selector {
+ case 0:
+ 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 MatchingType or Selector")
+}
diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go
new file mode 100644
index 0000000000..cf456165f4
--- /dev/null
+++ b/vendor/github.com/miekg/dns/defaults.go
@@ -0,0 +1,282 @@
+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 {
+ // EDNS0 is at the end of the additional section, start there.
+ // We might want to change this to *only* look at the last two
+ // records. So we see TSIG and/or OPT - this a slightly bigger
+ // change though.
+ for i := len(dns.Extra) - 1; i >= 0; i-- {
+ if dns.Extra[i].Header().Rrtype == TypeOPT {
+ return dns.Extra[i].(*OPT)
+ }
+ }
+ return nil
+}
+
+// IsDomainName checks if s is a valid domain name, it returns the number of
+// labels and true, when a domain name is valid. Note that non fully qualified
+// domain name is considered valid, in this case the last label is counted in
+// the number of labels. When false is returned the number of labels is not
+// defined. Also note that this function is extremely liberal; almost any
+// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
+// label fits in 63 characters, 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. If child and parent
+// are the same domain true is returned as well.
+func IsSubDomain(parent, child string) bool {
+ // Entire child is contained in parent
+ return CompareDomainName(parent, child) == CountLabel(parent)
+}
+
+// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
+// The checking is performed on the binary payload.
+func IsMsg(buf []byte) error {
+ // Header
+ if len(buf) < 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))
+}
diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go
new file mode 100644
index 0000000000..b3292287ce
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dns.go
@@ -0,0 +1,104 @@
+package dns
+
+import "strconv"
+
+const (
+ year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
+ defaultTtl = 3600 // Default internal TTL.
+
+ DefaultMsgSize = 4096 // DefaultMsgSize is the standard default for messages larger than 512 bytes.
+ MinMsgSize = 512 // MinMsgSize is the minimal size of a DNS packet.
+ MaxMsgSize = 65535 // MaxMsgSize is the largest possible DNS packet.
+)
+
+// Error represents a DNS error.
+type Error struct{ err string }
+
+func (e *Error) Error() string {
+ if e == nil {
+ return "dns: "
+ }
+ 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
+ // pack packs an RR into wire format.
+ pack([]byte, int, map[string]int, bool) (int, error)
+}
+
+// RR_Header is the header all DNS resource records share.
+type RR_Header struct {
+ Name string `dns:"cdomain-name"`
+ Rrtype uint16
+ Class uint16
+ Ttl uint32
+ Rdlength uint16 // Length of data after header.
+}
+
+// Header returns itself. This is here to make RR_Header implements the RR interface.
+func (h *RR_Header) Header() *RR_Header { return h }
+
+// Just to implement the RR interface.
+func (h *RR_Header) copy() RR { return nil }
+
+func (h *RR_Header) 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 := PackRR(r, buf, 0, nil, false)
+ if err != nil {
+ return err
+ }
+ buf = buf[:off]
+ if int(r.Header().Rdlength) > off {
+ return ErrBuf
+ }
+
+ rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength))
+ if err != nil {
+ return err
+ }
+ *rr = *rfc3597.(*RFC3597)
+ return nil
+}
diff --git a/vendor/github.com/miekg/dns/dns_bench_test.go b/vendor/github.com/miekg/dns/dns_bench_test.go
new file mode 100644
index 0000000000..bccc3d5404
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dns_bench_test.go
@@ -0,0 +1,211 @@
+package dns
+
+import (
+ "net"
+ "testing"
+)
+
+func BenchmarkMsgLength(b *testing.B) {
+ b.StopTimer()
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ msg.Len()
+ }
+}
+
+func BenchmarkMsgLengthPack(b *testing.B) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = msg.Pack()
+ }
+}
+
+func BenchmarkPackDomainName(b *testing.B) {
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ buf := make([]byte, len(name1)+1)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackDomainName(name1, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackDomainName(b *testing.B) {
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ buf := make([]byte, len(name1)+1)
+ _, _ = PackDomainName(name1, buf, 0, nil, false)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackDomainName(buf, 0)
+ }
+}
+
+func BenchmarkUnpackDomainNameUnprintable(b *testing.B) {
+ name1 := "\x02\x02\x02\x025\x02\x02\x02\x02.12345678.123."
+ buf := make([]byte, len(name1)+1)
+ _, _ = PackDomainName(name1, buf, 0, nil, false)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackDomainName(buf, 0)
+ }
+}
+
+func BenchmarkCopy(b *testing.B) {
+ b.ReportAllocs()
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeA)
+ rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1")
+ m.Answer = []RR{rr}
+ rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1")
+ m.Ns = []RR{rr}
+ rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.1")
+ m.Extra = []RR{rr}
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.Copy()
+ }
+}
+
+func BenchmarkPackA(b *testing.B) {
+ a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
+
+ buf := make([]byte, a.len())
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackRR(a, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackA(b *testing.B) {
+ a := &A{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, A: net.IPv4(127, 0, 0, 1)}
+
+ buf := make([]byte, a.len())
+ PackRR(a, buf, 0, nil, false)
+ a = nil
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackRR(buf, 0)
+ }
+}
+
+func BenchmarkPackMX(b *testing.B) {
+ m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
+
+ buf := make([]byte, m.len())
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackRR(m, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackMX(b *testing.B) {
+ m := &MX{Hdr: RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY}, Mx: "mx.miek.nl."}
+
+ buf := make([]byte, m.len())
+ PackRR(m, buf, 0, nil, false)
+ m = nil
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackRR(buf, 0)
+ }
+}
+
+func BenchmarkPackAAAAA(b *testing.B) {
+ aaaa, _ := NewRR(". IN A ::1")
+
+ buf := make([]byte, aaaa.len())
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = PackRR(aaaa, buf, 0, nil, false)
+ }
+}
+
+func BenchmarkUnpackAAAA(b *testing.B) {
+ aaaa, _ := NewRR(". IN A ::1")
+
+ buf := make([]byte, aaaa.len())
+ PackRR(aaaa, buf, 0, nil, false)
+ aaaa = nil
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _, _ = UnpackRR(buf, 0)
+ }
+}
+
+func BenchmarkPackMsg(b *testing.B) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ buf := make([]byte, 512)
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = msg.PackBuffer(buf)
+ }
+}
+
+func BenchmarkUnpackMsg(b *testing.B) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
+ msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
+ msgBuf, _ := msg.Pack()
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = msg.Unpack(msgBuf)
+ }
+}
+
+func BenchmarkIdGeneration(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = id()
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dns_test.go b/vendor/github.com/miekg/dns/dns_test.go
new file mode 100644
index 0000000000..ad68533fd5
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dns_test.go
@@ -0,0 +1,433 @@
+package dns
+
+import (
+ "encoding/hex"
+ "net"
+ "testing"
+)
+
+func TestPackUnpack(t *testing.T) {
+ out := new(Msg)
+ out.Answer = make([]RR, 1)
+ key := new(DNSKEY)
+ key = &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1}
+ key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}
+ key.PublicKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
+
+ out.Answer[0] = key
+ msg, err := out.Pack()
+ if err != nil {
+ t.Error("failed to pack msg with DNSKEY")
+ }
+ in := new(Msg)
+ if in.Unpack(msg) != nil {
+ t.Error("failed to unpack msg with DNSKEY")
+ }
+
+ sig := new(RRSIG)
+ sig = &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2,
+ OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.",
+ Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"}
+ sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}
+
+ out.Answer[0] = sig
+ msg, err = out.Pack()
+ if err != nil {
+ t.Error("failed to pack msg with RRSIG")
+ }
+
+ if in.Unpack(msg) != nil {
+ t.Error("failed to unpack msg with RRSIG")
+ }
+}
+
+func TestPackUnpack2(t *testing.T) {
+ m := new(Msg)
+ m.Extra = make([]RR, 1)
+ m.Answer = make([]RR, 1)
+ dom := "miek.nl."
+ rr := new(A)
+ rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
+ rr.A = net.IPv4(127, 0, 0, 1)
+
+ x := new(TXT)
+ x.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
+ x.Txt = []string{"heelalaollo"}
+
+ m.Extra[0] = x
+ m.Answer[0] = rr
+ _, err := m.Pack()
+ if err != nil {
+ t.Error("Packing failed: ", err)
+ return
+ }
+}
+
+func TestPackUnpack3(t *testing.T) {
+ m := new(Msg)
+ m.Extra = make([]RR, 2)
+ m.Answer = make([]RR, 1)
+ dom := "miek.nl."
+ rr := new(A)
+ rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
+ rr.A = net.IPv4(127, 0, 0, 1)
+
+ x1 := new(TXT)
+ x1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
+ x1.Txt = []string{}
+
+ x2 := new(TXT)
+ x2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
+ x2.Txt = []string{"heelalaollo"}
+
+ m.Extra[0] = x1
+ m.Extra[1] = x2
+ m.Answer[0] = rr
+ b, err := m.Pack()
+ if err != nil {
+ t.Error("packing failed: ", err)
+ return
+ }
+
+ var unpackMsg Msg
+ err = unpackMsg.Unpack(b)
+ if err != nil {
+ t.Error("unpacking failed")
+ return
+ }
+}
+
+func TestBailiwick(t *testing.T) {
+ yes := map[string]string{
+ "miek1.nl": "miek1.nl",
+ "miek.nl": "ns.miek.nl",
+ ".": "miek.nl",
+ }
+ for parent, child := range yes {
+ if !IsSubDomain(parent, child) {
+ t.Errorf("%s should be child of %s", child, parent)
+ t.Errorf("comparelabels %d", CompareDomainName(parent, child))
+ t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
+ }
+ }
+ no := map[string]string{
+ "www.miek.nl": "ns.miek.nl",
+ "m\\.iek.nl": "ns.miek.nl",
+ "w\\.iek.nl": "w.iek.nl",
+ "p\\\\.iek.nl": "ns.p.iek.nl", // p\\.iek.nl , literal \ in domain name
+ "miek.nl": ".",
+ }
+ for parent, child := range no {
+ if IsSubDomain(parent, child) {
+ t.Errorf("%s should not be child of %s", child, parent)
+ t.Errorf("comparelabels %d", CompareDomainName(parent, child))
+ t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
+ }
+ }
+}
+
+func TestPack(t *testing.T) {
+ rr := []string{"US. 86400 IN NSEC 0-.us. NS SOA RRSIG NSEC DNSKEY TYPE65534"}
+ m := new(Msg)
+ var err error
+ m.Answer = make([]RR, 1)
+ for _, r := range rr {
+ m.Answer[0], err = NewRR(r)
+ if err != nil {
+ t.Errorf("failed to create RR: %v", err)
+ continue
+ }
+ if _, err := m.Pack(); err != nil {
+ t.Errorf("packing failed: %v", err)
+ }
+ }
+ x := new(Msg)
+ ns, _ := NewRR("pool.ntp.org. 390 IN NS a.ntpns.org")
+ ns.(*NS).Ns = "a.ntpns.org"
+ x.Ns = append(m.Ns, ns)
+ x.Ns = append(m.Ns, ns)
+ x.Ns = append(m.Ns, ns)
+ // This crashes due to the fact the a.ntpns.org isn't a FQDN
+ // How to recover() from a remove panic()?
+ if _, err := x.Pack(); err == nil {
+ t.Error("packing should fail")
+ }
+ x.Answer = make([]RR, 1)
+ x.Answer[0], err = NewRR(rr[0])
+ if _, err := x.Pack(); err == nil {
+ t.Error("packing should fail")
+ }
+ x.Question = make([]Question, 1)
+ x.Question[0] = Question{";sd#eddddséâèµââ
â¥âxzztsestxssweewwsssstx@s@Zåµe@cn.pool.ntp.org.", TypeA, ClassINET}
+ if _, err := x.Pack(); err == nil {
+ t.Error("packing should fail")
+ }
+}
+
+func TestPackNAPTR(t *testing.T) {
+ for _, n := range []string{
+ `apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`,
+ `apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`,
+ `apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`,
+ } {
+ rr, _ := NewRR(n)
+ msg := make([]byte, rr.len())
+ if off, err := PackRR(rr, msg, 0, nil, false); err != nil {
+ t.Errorf("packing failed: %v", err)
+ t.Errorf("length %d, need more than %d", rr.len(), off)
+ } else {
+ t.Logf("buf size needed: %d", off)
+ }
+ }
+}
+
+func TestCompressLength(t *testing.T) {
+ m := new(Msg)
+ m.SetQuestion("miek.nl", TypeMX)
+ ul := m.Len()
+ m.Compress = true
+ if ul != m.Len() {
+ t.Fatalf("should be equal")
+ }
+}
+
+// Does the predicted length match final packed length?
+func TestMsgCompressLength(t *testing.T) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ msg.Compress = true
+ return msg
+ }
+
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1")
+ rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
+ tests := []*Msg{
+ makeMsg(name1, []RR{rrA}, nil, nil),
+ makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
+
+ for _, msg := range tests {
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted < len(buf) {
+ t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
+ msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
+ }
+ }
+}
+
+func TestMsgLength(t *testing.T) {
+ makeMsg := func(question string, ans, ns, e []RR) *Msg {
+ msg := new(Msg)
+ msg.SetQuestion(Fqdn(question), TypeANY)
+ msg.Answer = append(msg.Answer, ans...)
+ msg.Ns = append(msg.Ns, ns...)
+ msg.Extra = append(msg.Extra, e...)
+ return msg
+ }
+
+ name1 := "12345678901234567890123456789012345.12345678.123."
+ rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1")
+ rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
+ tests := []*Msg{
+ makeMsg(name1, []RR{rrA}, nil, nil),
+ makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
+
+ for _, msg := range tests {
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted < len(buf) {
+ t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d",
+ msg.Question[0].Name, predicted, len(buf))
+ }
+ }
+}
+
+func TestMsgLength2(t *testing.T) {
+ // Serialized replies
+ var testMessages = []string{
+ // google.com. IN A?
+ "064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000",
+ // amazon.com. IN A? (reply has no EDNS0 record)
+ // TODO(miek): this one is off-by-one, need to find out why
+ //"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001",
+ // yahoo.com. IN A?
+ "fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000",
+ // microsoft.com. IN A?
+ "f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000",
+ // google.com. IN MX?
+ "724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000",
+ // reddit.com. IN A?
+ "12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000",
+ }
+
+ for i, hexData := range testMessages {
+ // we won't fail the decoding of the hex
+ input, _ := hex.DecodeString(hexData)
+
+ m := new(Msg)
+ m.Unpack(input)
+ m.Compress = true
+ lenComp := m.Len()
+ b, _ := m.Pack()
+ pacComp := len(b)
+ m.Compress = false
+ lenUnComp := m.Len()
+ b, _ = m.Pack()
+ pacUnComp := len(b)
+ if pacComp+1 != lenComp {
+ t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i)
+ }
+ if pacUnComp+1 != lenUnComp {
+ t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i)
+ }
+ }
+}
+
+func TestMsgLengthCompressionMalformed(t *testing.T) {
+ // SOA with empty hostmaster, which is illegal
+ soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},
+ Ns: ".",
+ Mbox: "",
+ Serial: 0,
+ Refresh: 28800,
+ Retry: 7200,
+ Expire: 604800,
+ Minttl: 60}
+ m := new(Msg)
+ m.Compress = true
+ m.Ns = []RR{soa}
+ m.Len() // Should not crash.
+}
+
+func TestToRFC3597(t *testing.T) {
+ a, _ := NewRR("miek.nl. IN A 10.0.1.1")
+ x := new(RFC3597)
+ x.ToRFC3597(a)
+ if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` {
+ t.Errorf("string mismatch, got: %s", x)
+ }
+
+ b, _ := NewRR("miek.nl. IN MX 10 mx.miek.nl.")
+ x.ToRFC3597(b)
+ if x.String() != `miek.nl. 3600 CLASS1 TYPE15 \# 14 000a026d78046d69656b026e6c00` {
+ t.Errorf("string mismatch, got: %s", x)
+ }
+}
+
+func TestNoRdataPack(t *testing.T) {
+ data := make([]byte, 1024)
+ for typ, fn := range TypeToRR {
+ r := fn()
+ *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
+ _, err := PackRR(r, data, 0, nil, false)
+ if err != nil {
+ t.Errorf("failed to pack RR with zero rdata: %s: %v", TypeToString[typ], err)
+ }
+ }
+}
+
+func TestNoRdataUnpack(t *testing.T) {
+ data := make([]byte, 1024)
+ for typ, fn := range TypeToRR {
+ if typ == TypeSOA || typ == TypeTSIG {
+ // SOA, TSIG will not be seen (like this) in dyn. updates?
+ continue
+ }
+ r := fn()
+ *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 16}
+ off, err := PackRR(r, data, 0, nil, false)
+ if err != nil {
+ // Should always works, TestNoDataPack should have caught this
+ t.Errorf("failed to pack RR: %v", err)
+ continue
+ }
+ rr, _, err := UnpackRR(data[:off], 0)
+ if err != nil {
+ t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err)
+ }
+ t.Log(rr)
+ }
+}
+
+func TestRdataOverflow(t *testing.T) {
+ rr := new(RFC3597)
+ rr.Hdr.Name = "."
+ rr.Hdr.Class = ClassINET
+ rr.Hdr.Rrtype = 65280
+ rr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF))
+ buf := make([]byte, 0xFFFF*2)
+ if _, err := PackRR(rr, buf, 0, nil, false); err != nil {
+ t.Fatalf("maximum size rrdata pack failed: %v", err)
+ }
+ rr.Rdata += "00"
+ if _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata {
+ t.Fatalf("oversize rrdata pack didn't return ErrRdata - instead: %v", err)
+ }
+}
+
+func TestCopy(t *testing.T) {
+ rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL
+ rr1 := Copy(rr)
+ if rr.String() != rr1.String() {
+ t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String())
+ }
+}
+
+func TestMsgCopy(t *testing.T) {
+ m := new(Msg)
+ m.SetQuestion("miek.nl.", TypeA)
+ rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1")
+ m.Answer = []RR{rr}
+ rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1")
+ m.Ns = []RR{rr}
+
+ m1 := m.Copy()
+ if m.String() != m1.String() {
+ t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String())
+ }
+
+ m1.Answer[0], _ = NewRR("somethingelse.nl. 2311 IN A 127.0.0.1")
+ if m.String() == m1.String() {
+ t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String())
+ }
+
+ rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.2")
+ m1.Answer = append(m1.Answer, rr)
+ if m1.Ns[0].String() == m1.Answer[1].String() {
+ t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String())
+ }
+}
+
+func TestMsgPackBuffer(t *testing.T) {
+ var testMessages = []string{
+ // news.ycombinator.com.in.escapemg.com. IN A, response
+ "586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10",
+
+ // news.ycombinator.com.in.escapemg.com. IN A, question
+ "586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001",
+
+ "398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001",
+ }
+
+ for i, hexData := range testMessages {
+ // we won't fail the decoding of the hex
+ input, _ := hex.DecodeString(hexData)
+ m := new(Msg)
+ if err := m.Unpack(input); err != nil {
+ t.Errorf("packet %d failed to unpack", i)
+ continue
+ }
+ t.Logf("packet %d %s", i, m.String())
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go
new file mode 100644
index 0000000000..f5f3fbdd89
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnssec.go
@@ -0,0 +1,721 @@
+package dns
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/dsa"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ _ "crypto/md5"
+ "crypto/rand"
+ "crypto/rsa"
+ _ "crypto/sha1"
+ _ "crypto/sha256"
+ _ "crypto/sha512"
+ "encoding/asn1"
+ "encoding/binary"
+ "encoding/hex"
+ "math/big"
+ "sort"
+ "strings"
+ "time"
+)
+
+// 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.
+type rrsigWireFmt struct {
+ TypeCovered uint16
+ Algorithm uint8
+ Labels uint8
+ OrigTtl uint32
+ Expiration uint32
+ Inception uint32
+ KeyTag uint16
+ SignerName string `dns:"domain-name"`
+ /* No Signature */
+}
+
+// Used for converting DNSKEY's rdata to wirefmt.
+type dnskeyWireFmt struct {
+ Flags uint16
+ Protocol uint8
+ Algorithm uint8
+ PublicKey string `dns:"base64"`
+ /* Nothing is left out */
+}
+
+func divRoundUp(a, b int) int {
+ return (a + b - 1) / b
+}
+
+// KeyTag calculates the keytag (or key-id) of the DNSKEY.
+func (k *DNSKEY) KeyTag() uint16 {
+ if k == nil {
+ return 0
+ }
+ var keytag int
+ switch k.Algorithm {
+ case RSAMD5:
+ // Look at the bottom two bytes of the modules, which the last
+ // item in the pubkey. We could do this faster by looking directly
+ // at the base64 values. But I'm lazy.
+ modulus, _ := fromBase64([]byte(k.PublicKey))
+ if len(modulus) > 1 {
+ x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
+ keytag = int(x)
+ }
+ default:
+ keywire := new(dnskeyWireFmt)
+ keywire.Flags = k.Flags
+ keywire.Protocol = k.Protocol
+ keywire.Algorithm = k.Algorithm
+ keywire.PublicKey = k.PublicKey
+ wire := make([]byte, DefaultMsgSize)
+ n, err := packKeyWire(keywire, wire)
+ if err != nil {
+ return 0
+ }
+ wire = wire[:n]
+ for i, v := range wire {
+ if i&1 != 0 {
+ keytag += int(v) // must be larger than uint32
+ } else {
+ keytag += int(v) << 8
+ }
+ }
+ keytag += (keytag >> 16) & 0xFFFF
+ keytag &= 0xFFFF
+ }
+ return uint16(keytag)
+}
+
+// ToDS converts a DNSKEY record to a DS record.
+func (k *DNSKEY) ToDS(h uint8) *DS {
+ if k == nil {
+ return nil
+ }
+ ds := new(DS)
+ ds.Hdr.Name = k.Hdr.Name
+ ds.Hdr.Class = k.Hdr.Class
+ ds.Hdr.Rrtype = TypeDS
+ ds.Hdr.Ttl = k.Hdr.Ttl
+ ds.Algorithm = k.Algorithm
+ ds.DigestType = h
+ ds.KeyTag = k.KeyTag()
+
+ keywire := new(dnskeyWireFmt)
+ keywire.Flags = k.Flags
+ keywire.Protocol = k.Protocol
+ keywire.Algorithm = k.Algorithm
+ keywire.PublicKey = k.PublicKey
+ wire := make([]byte, DefaultMsgSize)
+ n, err := packKeyWire(keywire, wire)
+ if err != nil {
+ return nil
+ }
+ wire = wire[:n]
+
+ owner := make([]byte, 255)
+ off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false)
+ if err1 != nil {
+ return nil
+ }
+ owner = owner[:off]
+ // RFC4034:
+ // digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
+ // "|" denotes concatenation
+ // DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
+
+ // 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 := packSigWire(sigwire, signdata)
+ 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 := packSigWire(sigwire, signeddata)
+ 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
+}
+
+func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {
+ // copied from zmsg.go RRSIG packing
+ off, err := packUint16(sw.TypeCovered, msg, 0)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint8(sw.Algorithm, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint8(sw.Labels, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint32(sw.OrigTtl, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint32(sw.Expiration, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint32(sw.Inception, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint16(sw.KeyTag, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = PackDomainName(sw.SignerName, msg, off, nil, false)
+ if err != nil {
+ return off, err
+ }
+ return off, nil
+}
+
+func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {
+ // copied from zmsg.go DNSKEY packing
+ off, err := packUint16(dw.Flags, msg, 0)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint8(dw.Protocol, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packUint8(dw.Algorithm, msg, off)
+ if err != nil {
+ return off, err
+ }
+ off, err = packStringBase64(dw.PublicKey, msg, off)
+ if err != nil {
+ return off, err
+ }
+ return off, nil
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go
new file mode 100644
index 0000000000..229a079370
--- /dev/null
+++ b/vendor/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
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go
new file mode 100644
index 0000000000..9ff3a617ef
--- /dev/null
+++ b/vendor/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 == "" || s[len(s)-1] != '\n' { // We need a closing newline
+ return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
+ }
+ return k.ReadPrivateKey(strings.NewReader(s), "")
+}
+
+// ReadPrivateKey reads a private key from the io.Reader q. The string file is
+// only used in error reporting.
+// The public key must be known, because some cryptographic algorithms embed
+// the public inside the privatekey.
+func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
+ m, err := parseKey(q, file)
+ if m == nil {
+ return nil, err
+ }
+ if _, ok := m["private-key-format"]; !ok {
+ return nil, ErrPrivKey
+ }
+ if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
+ return nil, ErrPrivKey
+ }
+ // TODO(mg): check if the pubkey matches the private key
+ algo, err := strconv.Atoi(strings.SplitN(m["algorithm"], " ", 2)[0])
+ if err != nil {
+ return nil, ErrPrivKey
+ }
+ switch uint8(algo) {
+ case DSA:
+ priv, err := readPrivateKeyDSA(m)
+ if err != nil {
+ return nil, err
+ }
+ pub := k.publicKeyDSA()
+ if pub == nil {
+ return nil, ErrKey
+ }
+ priv.PublicKey = *pub
+ return priv, nil
+ case RSAMD5:
+ fallthrough
+ case RSASHA1:
+ fallthrough
+ case RSASHA1NSEC3SHA1:
+ fallthrough
+ case RSASHA256:
+ fallthrough
+ case RSASHA512:
+ priv, err := readPrivateKeyRSA(m)
+ if err != nil {
+ return nil, err
+ }
+ pub := k.publicKeyRSA()
+ if pub == nil {
+ return nil, ErrKey
+ }
+ priv.PublicKey = *pub
+ return priv, nil
+ case ECCGOST:
+ return nil, ErrPrivKey
+ case ECDSAP256SHA256:
+ fallthrough
+ case ECDSAP384SHA384:
+ priv, err := readPrivateKeyECDSA(m)
+ if err != nil {
+ return nil, err
+ }
+ pub := k.publicKeyECDSA()
+ if pub == nil {
+ return nil, ErrKey
+ }
+ priv.PublicKey = *pub
+ return priv, nil
+ 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
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go
new file mode 100644
index 0000000000..56f3ea934f
--- /dev/null
+++ b/vendor/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 ""
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dnssec_test.go b/vendor/github.com/miekg/dns/dnssec_test.go
new file mode 100644
index 0000000000..ca085ed3b8
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnssec_test.go
@@ -0,0 +1,733 @@
+package dns
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rsa"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+func getKey() *DNSKEY {
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+ return key
+}
+
+func getSoa() *SOA {
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+ return soa
+}
+
+func TestGenerateEC(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = ECDSAP256SHA256
+ privkey, _ := key.Generate(256)
+ t.Log(key.String())
+ t.Log(key.PrivateKeyString(privkey))
+}
+
+func TestGenerateDSA(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = DSA
+ privkey, _ := key.Generate(1024)
+ t.Log(key.String())
+ t.Log(key.PrivateKeyString(privkey))
+}
+
+func TestGenerateRSA(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ privkey, _ := key.Generate(1024)
+ t.Log(key.String())
+ t.Log(key.PrivateKeyString(privkey))
+}
+
+func TestSecure(t *testing.T) {
+ soa := getSoa()
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = TypeSOA
+ sig.Algorithm = RSASHA256
+ sig.Labels = 2
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.OrigTtl = 14400
+ sig.KeyTag = 12051
+ sig.SignerName = "miek.nl."
+ sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M="
+
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+
+ // It should validate. Period is checked separately, so this will keep on working
+ if sig.Verify(key, []RR{soa}) != nil {
+ t.Error("failure to validate")
+ }
+}
+
+func TestSignature(t *testing.T) {
+ sig := new(RRSIG)
+ sig.Hdr.Name = "miek.nl."
+ sig.Hdr.Class = ClassINET
+ sig.Hdr.Ttl = 3600
+ sig.TypeCovered = TypeDNSKEY
+ sig.Algorithm = RSASHA1
+ sig.Labels = 2
+ sig.OrigTtl = 4000
+ sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
+ sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
+ sig.KeyTag = 34641
+ sig.SignerName = "miek.nl."
+ sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
+
+ // Should not be valid
+ if sig.ValidityPeriod(time.Now()) {
+ t.Error("should not be valid")
+ }
+
+ sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980
+ sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100
+ if !sig.ValidityPeriod(time.Now()) {
+ t.Error("should be valid")
+ }
+}
+
+func TestSignVerify(t *testing.T) {
+ // The record we want to sign
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+
+ soa1 := new(SOA)
+ soa1.Hdr = RR_Header{"*.miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa1.Ns = "open.nlnetlabs.nl."
+ soa1.Mbox = "miekg.atoom.net."
+ soa1.Serial = 1293945905
+ soa1.Refresh = 14400
+ soa1.Retry = 3600
+ soa1.Expire = 604800
+ soa1.Minttl = 86400
+
+ srv := new(SRV)
+ srv.Hdr = RR_Header{"srv.miek.nl.", TypeSRV, ClassINET, 14400, 0}
+ srv.Port = 1000
+ srv.Weight = 800
+ srv.Target = "web1.miek.nl."
+
+ hinfo := &HINFO{
+ Hdr: RR_Header{
+ Name: "miek.nl.",
+ Rrtype: TypeHINFO,
+ Class: ClassINET,
+ Ttl: 3789,
+ },
+ Cpu: "X",
+ Os: "Y",
+ }
+
+ // With this key
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ privkey, _ := key.Generate(512)
+
+ // Fill in the values of the Sig, before signing
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = soa.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3
+ sig.OrigTtl = soa.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = RSASHA256
+
+ for _, r := range []RR{soa, soa1, srv, hinfo} {
+ if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}); err != nil {
+ t.Error("failure to sign the record:", err)
+ continue
+ }
+ if err := sig.Verify(key, []RR{r}); err != nil {
+ t.Error("failure to validate")
+ continue
+ }
+ t.Logf("validated: %s", r.Header().Name)
+ }
+}
+
+func Test65534(t *testing.T) {
+ t6 := new(RFC3597)
+ t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0}
+ t6.Rdata = "505D870001"
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ privkey, _ := key.Generate(1024)
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = t6.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(t6.Hdr.Name))
+ sig.OrigTtl = t6.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag()
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = RSASHA256
+ if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil {
+ t.Error(err)
+ t.Error("failure to sign the TYPE65534 record")
+ }
+ if err := sig.Verify(key, []RR{t6}); err != nil {
+ t.Error(err)
+ t.Error("failure to validate")
+ } else {
+ t.Logf("validated: %s", t6.Header().Name)
+ }
+}
+
+func TestDnskey(t *testing.T) {
+ pubkey, err := ReadRR(strings.NewReader(`
+miek.nl. IN DNSKEY 256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b}
+`), "Kmiek.nl.+010+05240.key")
+ if err != nil {
+ t.Fatal(err)
+ }
+ privStr := `Private-key-format: v1.3
+Algorithm: 10 (RSASHA512)
+Modulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs=
+PublicExponent: AQAB
+PrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE=
+Prime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ==
+Prime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw==
+Exponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ==
+Exponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw==
+Coefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ==
+`
+ privkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr),
+ "Kmiek.nl.+010+05240.private")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" {
+ t.Error("pubkey is not what we've read")
+ }
+ if pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr {
+ t.Error("privkey is not what we've read")
+ t.Errorf("%v", pubkey.(*DNSKEY).PrivateKeyString(privkey))
+ }
+}
+
+func TestTag(t *testing.T) {
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 3600
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+
+ tag := key.KeyTag()
+ if tag != 12051 {
+ t.Errorf("wrong key tag: %d for key %v", tag, key)
+ }
+}
+
+func TestKeyRSA(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 3600
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ priv, _ := key.Generate(2048)
+
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = TypeSOA
+ sig.Algorithm = RSASHA256
+ sig.Labels = 2
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.OrigTtl = soa.Hdr.Ttl
+ sig.KeyTag = key.KeyTag()
+ sig.SignerName = key.Hdr.Name
+
+ if err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil {
+ t.Error("failed to sign")
+ return
+ }
+ if err := sig.Verify(key, []RR{soa}); err != nil {
+ t.Error("failed to verify")
+ }
+}
+
+func TestKeyToDS(t *testing.T) {
+ key := new(DNSKEY)
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 3600
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = RSASHA256
+ key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
+
+ ds := key.ToDS(SHA1)
+ if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" {
+ t.Errorf("wrong DS digest for SHA1\n%v", ds)
+ }
+}
+
+func TestSignRSA(t *testing.T) {
+ pub := "miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ"
+
+ priv := `Private-key-format: v1.3
+Algorithm: 5 (RSASHA1)
+Modulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k=
+PublicExponent: AQAB
+PrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk=
+Prime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw==
+Prime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw==
+Exponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ==
+Exponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw==
+Coefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw==
+Created: 20110302104537
+Publish: 20110302104537
+Activate: 20110302104537`
+
+ xk, _ := NewRR(pub)
+ k := xk.(*DNSKEY)
+ p, err := k.NewPrivateKey(priv)
+ if err != nil {
+ t.Error(err)
+ }
+ switch priv := p.(type) {
+ case *rsa.PrivateKey:
+ if 65537 != priv.PublicKey.E {
+ t.Error("exponenent should be 65537")
+ }
+ default:
+ t.Errorf("we should have read an RSA key: %v", priv)
+ }
+ if k.KeyTag() != 37350 {
+ t.Errorf("keytag should be 37350, got %d %v", k.KeyTag(), k)
+ }
+
+ soa := new(SOA)
+ soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
+ soa.Ns = "open.nlnetlabs.nl."
+ soa.Mbox = "miekg.atoom.net."
+ soa.Serial = 1293945905
+ soa.Refresh = 14400
+ soa.Retry = 3600
+ soa.Expire = 604800
+ soa.Minttl = 86400
+
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = k.KeyTag()
+ sig.SignerName = k.Hdr.Name
+ sig.Algorithm = k.Algorithm
+
+ sig.Sign(p.(*rsa.PrivateKey), []RR{soa})
+ if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
+ t.Errorf("signature is not correct: %v", sig)
+ }
+}
+
+func TestSignVerifyECDSA(t *testing.T) {
+ pub := `example.net. 3600 IN DNSKEY 257 3 14 (
+ xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
+ w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
+ /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
+ priv := `Private-key-format: v1.2
+Algorithm: 14 (ECDSAP384SHA384)
+PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
+
+ eckey, err := NewRR(pub)
+ if err != nil {
+ t.Fatal(err)
+ }
+ privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // TODO: Create separate test for this
+ ds := eckey.(*DNSKEY).ToDS(SHA384)
+ if ds.KeyTag != 10771 {
+ t.Fatal("wrong keytag on DS")
+ }
+ if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" {
+ t.Fatal("wrong DS Digest")
+ }
+ a, _ := NewRR("www.example.net. 3600 IN A 192.0.2.1")
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.Expiration, _ = StringToTime("20100909102025")
+ sig.Inception, _ = StringToTime("20100812102025")
+ sig.KeyTag = eckey.(*DNSKEY).KeyTag()
+ sig.SignerName = eckey.(*DNSKEY).Hdr.Name
+ sig.Algorithm = eckey.(*DNSKEY).Algorithm
+
+ if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil {
+ t.Fatal("failure to sign the record")
+ }
+
+ if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
+ t.Fatalf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+ eckey.(*DNSKEY).String(),
+ a.String(),
+ sig.String(),
+ eckey.(*DNSKEY).PrivateKeyString(privkey),
+ err,
+ )
+ }
+}
+
+func TestSignVerifyECDSA2(t *testing.T) {
+ srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
+ if err != nil {
+ t.Fatal(err)
+ }
+ srv := srv1.(*SRV)
+
+ // With this key
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = ECDSAP256SHA256
+ privkey, err := key.Generate(256)
+ if err != nil {
+ t.Fatal("failure to generate key")
+ }
+
+ // Fill in the values of the Sig, before signing
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = srv.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
+ sig.OrigTtl = srv.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = ECDSAP256SHA256
+
+ if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil {
+ t.Fatal("failure to sign the record")
+ }
+
+ err = sig.Verify(key, []RR{srv})
+ if err != nil {
+ t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+ key.String(),
+ srv.String(),
+ sig.String(),
+ key.PrivateKeyString(privkey),
+ err,
+ )
+ }
+}
+
+// Here the test vectors from the relevant RFCs are checked.
+// rfc6605 6.1
+func TestRFC6605P256(t *testing.T) {
+ exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 (
+ GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb
+ krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )`
+ exPriv := `Private-key-format: v1.2
+Algorithm: 13 (ECDSAP256SHA256)
+PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
+ rrDNSKEY, err := NewRR(exDNSKEY)
+ if err != nil {
+ t.Fatal(err)
+ }
+ priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exDS := `example.net. 3600 IN DS 55648 13 2 (
+ b4c8c1fe2e7477127b27115656ad6256f424625bf5c1
+ e2770ce6d6e37df61d17 )`
+ rrDS, err := NewRR(exDS)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
+ if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
+ t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
+ }
+
+ exA := `www.example.net. 3600 IN A 192.0.2.1`
+ exRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 (
+ 20100909100439 20100812100439 55648 example.net.
+ qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA
+ yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )`
+ rrA, err := NewRR(exA)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rrRRSIG, err := NewRR(exRRSIG)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate the spec RRSIG: %v", err)
+ }
+
+ ourRRSIG := &RRSIG{
+ Hdr: RR_Header{
+ Ttl: rrA.Header().Ttl,
+ },
+ KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
+ SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
+ Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
+ }
+ ourRRSIG.Expiration, _ = StringToTime("20100909100439")
+ ourRRSIG.Inception, _ = StringToTime("20100812100439")
+ err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate our RRSIG: %v", err)
+ }
+
+ // Signatures are randomized
+ rrRRSIG.(*RRSIG).Signature = ""
+ ourRRSIG.Signature = ""
+ if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
+ t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
+ }
+}
+
+// rfc6605 6.2
+func TestRFC6605P384(t *testing.T) {
+ exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 (
+ xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
+ w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
+ /uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
+ exPriv := `Private-key-format: v1.2
+Algorithm: 14 (ECDSAP384SHA384)
+PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
+ rrDNSKEY, err := NewRR(exDNSKEY)
+ if err != nil {
+ t.Fatal(err)
+ }
+ priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exDS := `example.net. 3600 IN DS 10771 14 4 (
+ 72d7b62976ce06438e9c0bf319013cf801f09ecc84b8
+ d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94
+ 6df983d6 )`
+ rrDS, err := NewRR(exDS)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)
+ if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
+ t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
+ }
+
+ exA := `www.example.net. 3600 IN A 192.0.2.1`
+ exRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 (
+ 20100909102025 20100812102025 10771 example.net.
+ /L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP
+ 95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz
+ WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )`
+ rrA, err := NewRR(exA)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rrRRSIG, err := NewRR(exRRSIG)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate the spec RRSIG: %v", err)
+ }
+
+ ourRRSIG := &RRSIG{
+ Hdr: RR_Header{
+ Ttl: rrA.Header().Ttl,
+ },
+ KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
+ SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
+ Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
+ }
+ ourRRSIG.Expiration, _ = StringToTime("20100909102025")
+ ourRRSIG.Inception, _ = StringToTime("20100812102025")
+ err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
+ t.Errorf("failure to validate our RRSIG: %v", err)
+ }
+
+ // Signatures are randomized
+ rrRRSIG.(*RRSIG).Signature = ""
+ ourRRSIG.Signature = ""
+ if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
+ t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
+ }
+}
+
+func TestInvalidRRSet(t *testing.T) {
+ goodRecords := make([]RR, 2)
+ goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
+
+ // Generate key
+ keyname := "cloudflare.com."
+ key := &DNSKEY{
+ Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
+ Algorithm: ECDSAP256SHA256,
+ Flags: ZONE,
+ Protocol: 3,
+ }
+ privatekey, err := key.Generate(256)
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+
+ // Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
+ curTime := time.Now()
+ signature := &RRSIG{
+ Inception: uint32(curTime.Unix()),
+ Expiration: uint32(curTime.Add(time.Hour).Unix()),
+ KeyTag: key.KeyTag(),
+ SignerName: keyname,
+ Algorithm: ECDSAP256SHA256,
+ }
+
+ // Inconsistent name between records
+ badRecords := make([]RR, 2)
+ badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
+
+ if IsRRset(badRecords) {
+ t.Fatal("Record set with inconsistent names considered valid")
+ }
+
+ badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
+
+ if IsRRset(badRecords) {
+ t.Fatal("Record set with inconsistent record types considered valid")
+ }
+
+ badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
+ badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
+
+ if IsRRset(badRecords) {
+ t.Fatal("Record set with inconsistent record class considered valid")
+ }
+
+ // Sign the good record set and then make sure verification fails on the bad record set
+ if err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil {
+ t.Fatal("Signing good records failed")
+ }
+
+ if err := signature.Verify(key, badRecords); err != ErrRRset {
+ t.Fatal("Verification did not return ErrRRset with inconsistent records")
+ }
+}
diff --git a/vendor/github.com/miekg/dns/dnsutil/util.go b/vendor/github.com/miekg/dns/dnsutil/util.go
new file mode 100644
index 0000000000..9ed03f2969
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnsutil/util.go
@@ -0,0 +1,79 @@
+// Package dnsutil contains higher-level methods useful with the dns
+// package. While package dns implements the DNS protocols itself,
+// these functions are related but not directly required for protocol
+// processing. They are often useful in preparing input/output of the
+// functions in package dns.
+package dnsutil
+
+import (
+ "strings"
+
+ "github.com/miekg/dns"
+)
+
+// AddDomain adds origin to s if s is not already a FQDN.
+// Note that the result may not be a FQDN. If origin does not end
+// with a ".", the result won't either.
+// This implements the zonefile convention (specified in RFC 1035,
+// Section "5.1. Format") that "@" represents the
+// apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.".
+func AddOrigin(s, origin string) string {
+ // ("foo.", "origin.") -> "foo." (already a FQDN)
+ // ("foo", "origin.") -> "foo.origin."
+ // ("foo"), "origin" -> "foo.origin"
+ // ("@", "origin.") -> "origin." (@ represents the apex (bare) domain)
+ // ("", "origin.") -> "origin." (not obvious)
+ // ("foo", "") -> "foo" (not obvious)
+
+ if dns.IsFqdn(s) {
+ return s // s is already a FQDN, no need to mess with it.
+ }
+ if len(origin) == 0 {
+ return s // Nothing to append.
+ }
+ if s == "@" || len(s) == 0 {
+ return origin // Expand apex.
+ }
+
+ if origin == "." {
+ return s + origin // AddOrigin(s, ".") is an expensive way to add a ".".
+ }
+
+ return s + "." + origin // The simple case.
+}
+
+// TrimDomainName trims origin from s if s is a subdomain.
+// This function will never return "", but returns "@" instead (@ represents the apex (bare) domain).
+func TrimDomainName(s, origin string) string {
+ // An apex (bare) domain is always returned as "@".
+ // If the return value ends in a ".", the domain was not the suffix.
+ // origin can end in "." or not. Either way the results should be the same.
+
+ if len(s) == 0 {
+ return "@" // Return the apex (@) rather than "".
+ }
+ // Someone is using TrimDomainName(s, ".") to remove a dot if it exists.
+ if origin == "." {
+ return strings.TrimSuffix(s, origin)
+ }
+
+ // Dude, you aren't even if the right subdomain!
+ if !dns.IsSubDomain(origin, s) {
+ return s
+ }
+
+ slabels := dns.Split(s)
+ olabels := dns.Split(origin)
+ m := dns.CompareDomainName(s, origin)
+ if len(olabels) == m {
+ if len(olabels) == len(slabels) {
+ return "@" // origin == s
+ }
+ if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) {
+ return "@" // TrimDomainName(".foo.", "foo.")
+ }
+ }
+
+ // Return the first (len-m) labels:
+ return s[:slabels[len(slabels)-m]-1]
+}
diff --git a/vendor/github.com/miekg/dns/dnsutil/util_test.go b/vendor/github.com/miekg/dns/dnsutil/util_test.go
new file mode 100644
index 0000000000..0f1ecec8e0
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dnsutil/util_test.go
@@ -0,0 +1,130 @@
+package dnsutil
+
+import "testing"
+
+func TestAddOrigin(t *testing.T) {
+ var tests = []struct{ e1, e2, expected string }{
+ {"@", "example.com", "example.com"},
+ {"foo", "example.com", "foo.example.com"},
+ {"foo.", "example.com", "foo."},
+ {"@", "example.com.", "example.com."},
+ {"foo", "example.com.", "foo.example.com."},
+ {"foo.", "example.com.", "foo."},
+ // Oddball tests:
+ // In general origin should not be "" or "." but at least
+ // these tests verify we don't crash and will keep results
+ // from changing unexpectedly.
+ {"*.", "", "*."},
+ {"@", "", "@"},
+ {"foobar", "", "foobar"},
+ {"foobar.", "", "foobar."},
+ {"*.", ".", "*."},
+ {"@", ".", "."},
+ {"foobar", ".", "foobar."},
+ {"foobar.", ".", "foobar."},
+ }
+ for _, test := range tests {
+ actual := AddOrigin(test.e1, test.e2)
+ if test.expected != actual {
+ t.Errorf("AddOrigin(%#v, %#v) expected %#v, go %#v\n", test.e1, test.e2, test.expected, actual)
+ }
+ }
+}
+
+func TestTrimDomainName(t *testing.T) {
+
+ // Basic tests.
+ // Try trimming "example.com" and "example.com." from typical use cases.
+ var tests_examplecom = []struct{ experiment, expected string }{
+ {"foo.example.com", "foo"},
+ {"foo.example.com.", "foo"},
+ {".foo.example.com", ".foo"},
+ {".foo.example.com.", ".foo"},
+ {"*.example.com", "*"},
+ {"example.com", "@"},
+ {"example.com.", "@"},
+ {"com.", "com."},
+ {"foo.", "foo."},
+ {"serverfault.com.", "serverfault.com."},
+ {"serverfault.com", "serverfault.com"},
+ {".foo.ronco.com", ".foo.ronco.com"},
+ {".foo.ronco.com.", ".foo.ronco.com."},
+ }
+ for _, dom := range []string{"example.com", "example.com."} {
+ for i, test := range tests_examplecom {
+ actual := TrimDomainName(test.experiment, dom)
+ if test.expected != actual {
+ t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual)
+ }
+ }
+ }
+
+ // Paranoid tests.
+ // These test shouldn't be needed but I was weary of off-by-one errors.
+ // In theory, these can't happen because there are no single-letter TLDs,
+ // but it is good to exercize the code this way.
+ var tests = []struct{ experiment, expected string }{
+ {"", "@"},
+ {".", "."},
+ {"a.b.c.d.e.f.", "a.b.c.d.e"},
+ {"b.c.d.e.f.", "b.c.d.e"},
+ {"c.d.e.f.", "c.d.e"},
+ {"d.e.f.", "d.e"},
+ {"e.f.", "e"},
+ {"f.", "@"},
+ {".a.b.c.d.e.f.", ".a.b.c.d.e"},
+ {".b.c.d.e.f.", ".b.c.d.e"},
+ {".c.d.e.f.", ".c.d.e"},
+ {".d.e.f.", ".d.e"},
+ {".e.f.", ".e"},
+ {".f.", "@"},
+ {"a.b.c.d.e.f", "a.b.c.d.e"},
+ {"a.b.c.d.e.", "a.b.c.d.e."},
+ {"a.b.c.d.e", "a.b.c.d.e"},
+ {"a.b.c.d.", "a.b.c.d."},
+ {"a.b.c.d", "a.b.c.d"},
+ {"a.b.c.", "a.b.c."},
+ {"a.b.c", "a.b.c"},
+ {"a.b.", "a.b."},
+ {"a.b", "a.b"},
+ {"a.", "a."},
+ {"a", "a"},
+ {".a.b.c.d.e.f", ".a.b.c.d.e"},
+ {".a.b.c.d.e.", ".a.b.c.d.e."},
+ {".a.b.c.d.e", ".a.b.c.d.e"},
+ {".a.b.c.d.", ".a.b.c.d."},
+ {".a.b.c.d", ".a.b.c.d"},
+ {".a.b.c.", ".a.b.c."},
+ {".a.b.c", ".a.b.c"},
+ {".a.b.", ".a.b."},
+ {".a.b", ".a.b"},
+ {".a.", ".a."},
+ {".a", ".a"},
+ }
+ for _, dom := range []string{"f", "f."} {
+ for i, test := range tests {
+ actual := TrimDomainName(test.experiment, dom)
+ if test.expected != actual {
+ t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual)
+ }
+ }
+ }
+
+ // Test cases for bugs found in the wild.
+ // These test cases provide both origin, s, and the expected result.
+ // If you find a bug in the while, this is probably the easiest place
+ // to add it as a test case.
+ var tests_wild = []struct{ e1, e2, expected string }{
+ {"mathoverflow.net.", ".", "mathoverflow.net"},
+ {"mathoverflow.net", ".", "mathoverflow.net"},
+ {"", ".", "@"},
+ {"@", ".", "@"},
+ }
+ for i, test := range tests_wild {
+ actual := TrimDomainName(test.e1, test.e2)
+ if test.expected != actual {
+ t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.e1, test.e2, test.expected, actual)
+ }
+ }
+
+}
diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go
new file mode 100644
index 0000000000..e38753d7d8
--- /dev/null
+++ b/vendor/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 a request.
+
+ m := new(dns.Msg)
+ m.SetEdns0(4096, true)
+
+Signature generation, signature verification and key generation are all supported.
+
+DYNAMIC UPDATES
+
+Dynamic updates reuses the DNS message format, but renames three of
+the sections. Question is Zone, Answer is Prerequisite, Authority is
+Update, only the Additional is not renamed. See RFC 2136 for the gory details.
+
+You can set a rather complex set of rules for the existence of absence of
+certain resource records or names in a zone to specify if resource records
+should be added or removed. The table from RFC 2136 supplemented with the Go
+DNS function shows which functions exist to specify the prerequisites.
+
+ 3.2.4 - Table Of Metavalues Used In Prerequisite Section
+
+ CLASS TYPE RDATA Meaning Function
+ --------------------------------------------------------------
+ ANY ANY empty Name is in use dns.NameUsed
+ ANY rrset empty RRset exists (value indep) dns.RRsetUsed
+ NONE ANY empty Name is not in use dns.NameNotUsed
+ NONE rrset empty RRset does not exist dns.RRsetNotUsed
+ zone rrset rr RRset exists (value dep) dns.Used
+
+The prerequisite section can also be left empty.
+If you have decided on the prerequisites you can tell what RRs should
+be added or deleted. The next table shows the options you have and
+what functions to call.
+
+ 3.4.2.6 - Table Of Metavalues Used In Update Section
+
+ CLASS TYPE RDATA Meaning Function
+ ---------------------------------------------------------------
+ ANY ANY empty Delete all RRsets from name dns.RemoveName
+ ANY rrset empty Delete an RRset dns.RemoveRRset
+ NONE rrset rr Delete an RR from RRset dns.Remove
+ zone rrset rr Add to an RRset dns.Insert
+
+TRANSACTION SIGNATURE
+
+An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
+The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512.
+
+Basic use pattern when querying with a TSIG name "axfr." (note that these key names
+must be fully qualified - as they are domain names) and the base64 secret
+"so6ZGir4GPAqINNh9U5c3A==":
+
+ c := new(dns.Client)
+ c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
+ m := new(dns.Msg)
+ m.SetQuestion("miek.nl.", dns.TypeMX)
+ m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
+ ...
+ // When sending the TSIG RR is calculated and filled in before sending
+
+When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with
+TSIG, this is the basic use pattern. In this example we request an AXFR for
+miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
+and using the server 176.58.119.54:
+
+ t := new(dns.Transfer)
+ m := new(dns.Msg)
+ t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
+ m.SetAxfr("miek.nl.")
+ m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
+ c, err := t.In(m, "176.58.119.54:53")
+ for r := range c { ... }
+
+You can now read the records from the transfer as they come in. Each envelope is checked with TSIG.
+If something is not correct an error is returned.
+
+Basic use pattern validating and replying to a message that has TSIG set.
+
+ server := &dns.Server{Addr: ":53", Net: "udp"}
+ server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
+ go server.ListenAndServe()
+ dns.HandleFunc(".", handleRequest)
+
+ func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
+ m := new(dns.Msg)
+ m.SetReply(r)
+ if r.IsTsig() != nil {
+ if w.TsigStatus() == nil {
+ // *Msg r has an TSIG record and it was validated
+ m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
+ } else {
+ // *Msg r has an TSIG records and it was not valided
+ }
+ }
+ w.WriteMsg(m)
+ }
+
+PRIVATE RRS
+
+RFC 6895 sets aside a range of type codes for private use. This range
+is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
+can be used, before requesting an official type code from IANA.
+
+see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more
+information.
+
+EDNS0
+
+EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated
+by RFC 6891. It defines an new RR type, the OPT RR, which is then completely
+abused.
+Basic use pattern for creating an (empty) OPT RR:
+
+ o := new(dns.OPT)
+ o.Hdr.Name = "." // MUST be the root zone, per definition.
+ o.Hdr.Rrtype = dns.TypeOPT
+
+The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891)
+interfaces. Currently only a few have been standardized: EDNS0_NSID
+(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note
+that these options may be combined in an OPT RR.
+Basic use pattern for a server to check if (and which) options are set:
+
+ // o is a dns.OPT
+ for _, s := range o.Option {
+ switch e := s.(type) {
+ case *dns.EDNS0_NSID:
+ // do stuff with e.Nsid
+ case *dns.EDNS0_SUBNET:
+ // access e.Family, e.Address, etc.
+ }
+ }
+
+SIG(0)
+
+From RFC 2931:
+
+ SIG(0) provides protection for DNS transactions and requests ....
+ ... protection for glue records, DNS requests, protection for message headers
+ on requests and responses, and protection of the overall integrity of a response.
+
+It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared
+secret approach in TSIG.
+Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and
+RSASHA512.
+
+Signing subsequent messages in multi-message sessions is not implemented.
+*/
+package dns
diff --git a/vendor/github.com/miekg/dns/dyn_test.go b/vendor/github.com/miekg/dns/dyn_test.go
new file mode 100644
index 0000000000..09986a5e4e
--- /dev/null
+++ b/vendor/github.com/miekg/dns/dyn_test.go
@@ -0,0 +1,3 @@
+package dns
+
+// Find better solution
diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go
new file mode 100644
index 0000000000..465b85f37f
--- /dev/null
+++ b/vendor/github.com/miekg/dns/edns.go
@@ -0,0 +1,597 @@
+package dns
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "net"
+ "strconv"
+)
+
+// EDNS0 Option codes.
+const (
+ EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
+ EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
+ EDNS0NSID = 0x3 // nsid (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
+ EDNS0COOKIE = 0xa // EDNS0 Cookie
+ EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (RFC7828)
+ 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_COOKIE:
+ s += "\n; COOKIE: " + o.String()
+ case *EDNS0_UL:
+ s += "\n; UPDATE LEASE: " + o.String()
+ case *EDNS0_LLQ:
+ s += "\n; LONG LIVED QUERIES: " + o.String()
+ case *EDNS0_DAU:
+ s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String()
+ case *EDNS0_DHU:
+ s += "\n; DS HASH UNDERSTOOD: " + o.String()
+ case *EDNS0_N3U:
+ s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String()
+ case *EDNS0_LOCAL:
+ s += "\n; LOCAL OPT: " + o.String()
+ }
+ }
+ 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() int {
+ return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15
+}
+
+// SetExtendedRcode sets the EDNS extended RCODE field.
+func (rr *OPT) SetExtendedRcode(v uint8) {
+ if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have!
+ return
+ }
+ rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24)
+}
+
+// UDPSize returns the UDP buffer size.
+func (rr *OPT) UDPSize() uint16 {
+ return rr.Hdr.Class
+}
+
+// SetUDPSize sets the UDP buffer size.
+func (rr *OPT) SetUDPSize(size uint16) {
+ rr.Hdr.Class = size
+}
+
+// Do returns the value of the DO (DNSSEC OK) bit.
+func (rr *OPT) Do() bool {
+ return rr.Hdr.Ttl&_DO == _DO
+}
+
+// SetDo sets the DO (DNSSEC OK) bit.
+// If we pass an argument, set the DO bit to that value.
+// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored.
+func (rr *OPT) SetDo(do ...bool) {
+ if len(do) == 1 {
+ if do[0] {
+ rr.Hdr.Ttl |= _DO
+ } else {
+ rr.Hdr.Ttl &^= _DO
+ }
+ } else {
+ rr.Hdr.Ttl |= _DO
+ }
+}
+
+// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
+type EDNS0 interface {
+ // Option returns the option code for the option.
+ Option() uint16
+ // pack returns the bytes of the option data.
+ pack() ([]byte, error)
+ // unpack sets the data as found in the buffer. Is also sets
+ // the length of the slice as the length of the option data.
+ unpack([]byte) error
+ // String returns the string representation of the option.
+ String() string
+}
+
+// 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.SourceNetMask = 32 // 32 for IPV4, 128 for IPv6
+// e.SourceScope = 0
+// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
+// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
+// o.Option = append(o.Option, e)
+//
+// 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)
+ binary.BigEndian.PutUint16(b[0:], 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 = binary.BigEndian.Uint16(b)
+ 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 = ""
+ } 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 Cookie EDNS0 option
+//
+// o := new(dns.OPT)
+// o.Hdr.Name = "."
+// o.Hdr.Rrtype = dns.TypeOPT
+// e := new(dns.EDNS0_COOKIE)
+// e.Code = dns.EDNS0COOKIE
+// e.Cookie = "24a5ac.."
+// o.Option = append(o.Option, e)
+//
+// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is
+// always 8 bytes. It may then optionally be followed by the server cookie. The server
+// cookie is of variable length, 8 to a maximum of 32 bytes. In other words:
+//
+// cCookie := o.Cookie[:16]
+// sCookie := o.Cookie[16:]
+//
+// There is no guarantee that the Cookie string has a specific length.
+type EDNS0_COOKIE struct {
+ Code uint16 // Always EDNS0COOKIE
+ Cookie string // Hex-encoded cookie data
+}
+
+func (e *EDNS0_COOKIE) pack() ([]byte, error) {
+ h, err := hex.DecodeString(e.Cookie)
+ if err != nil {
+ return nil, err
+ }
+ return h, nil
+}
+
+func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
+func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
+func (e *EDNS0_COOKIE) String() string { return e.Cookie }
+
+// 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)
+ binary.BigEndian.PutUint32(b, e.Lease)
+ return b, nil
+}
+
+func (e *EDNS0_UL) unpack(b []byte) error {
+ if len(b) < 4 {
+ return ErrBuf
+ }
+ e.Lease = binary.BigEndian.Uint32(b)
+ return nil
+}
+
+// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
+// Implemented for completeness, as the EDNS0 type code is assigned.
+type EDNS0_LLQ struct {
+ Code uint16 // Always EDNS0LLQ
+ Version uint16
+ Opcode uint16
+ Error uint16
+ Id uint64
+ LeaseLife uint32
+}
+
+func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
+
+func (e *EDNS0_LLQ) pack() ([]byte, error) {
+ b := make([]byte, 18)
+ binary.BigEndian.PutUint16(b[0:], e.Version)
+ binary.BigEndian.PutUint16(b[2:], e.Opcode)
+ binary.BigEndian.PutUint16(b[4:], e.Error)
+ binary.BigEndian.PutUint64(b[6:], e.Id)
+ binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
+ return b, nil
+}
+
+func (e *EDNS0_LLQ) unpack(b []byte) error {
+ if len(b) < 18 {
+ return ErrBuf
+ }
+ e.Version = binary.BigEndian.Uint16(b[0:])
+ e.Opcode = binary.BigEndian.Uint16(b[2:])
+ e.Error = binary.BigEndian.Uint16(b[4:])
+ e.Id = binary.BigEndian.Uint64(b[6:])
+ e.LeaseLife = binary.BigEndian.Uint32(b[14:])
+ return nil
+}
+
+func (e *EDNS0_LLQ) String() string {
+ s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) +
+ " " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(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 = binary.BigEndian.Uint32(b)
+ return nil
+}
+
+// The EDNS0_LOCAL option is used for local/experimental purposes. The option
+// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
+// (RFC6891), although any unassigned code can actually be used. The content of
+// the option is made available in Data, unaltered.
+// Basic use pattern for creating a local option:
+//
+// o := new(dns.OPT)
+// o.Hdr.Name = "."
+// o.Hdr.Rrtype = dns.TypeOPT
+// e := new(dns.EDNS0_LOCAL)
+// e.Code = dns.EDNS0LOCALSTART
+// e.Data = []byte{72, 82, 74}
+// o.Option = append(o.Option, e)
+type EDNS0_LOCAL struct {
+ Code uint16
+ Data []byte
+}
+
+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
+}
+
+type EDNS0_TCP_KEEPALIVE struct {
+ Code uint16 // Always EDNSTCPKEEPALIVE
+ Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
+ Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order.
+}
+
+func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 {
+ return EDNS0TCPKEEPALIVE
+}
+
+func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
+ if e.Timeout != 0 && e.Length != 2 {
+ return nil, errors.New("dns: timeout specified but length is not 2")
+ }
+ if e.Timeout == 0 && e.Length != 0 {
+ return nil, errors.New("dns: timeout not specified but length is not 0")
+ }
+ b := make([]byte, 4+e.Length)
+ binary.BigEndian.PutUint16(b[0:], e.Code)
+ binary.BigEndian.PutUint16(b[2:], e.Length)
+ if e.Length == 2 {
+ binary.BigEndian.PutUint16(b[4:], e.Timeout)
+ }
+ return b, nil
+}
+
+func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
+ if len(b) < 4 {
+ return ErrBuf
+ }
+ e.Length = binary.BigEndian.Uint16(b[2:4])
+ if e.Length != 0 && e.Length != 2 {
+ return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
+ }
+ if e.Length == 2 {
+ if len(b) < 6 {
+ return ErrBuf
+ }
+ e.Timeout = binary.BigEndian.Uint16(b[4:6])
+ }
+ return nil
+}
+
+func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
+ s = "use tcp keep-alive"
+ if e.Length == 0 {
+ s += ", timeout omitted"
+ } else {
+ s += fmt.Sprintf(", timeout %dms", e.Timeout*100)
+ }
+ return
+}
diff --git a/vendor/github.com/miekg/dns/edns_test.go b/vendor/github.com/miekg/dns/edns_test.go
new file mode 100644
index 0000000000..c290b0c8a8
--- /dev/null
+++ b/vendor/github.com/miekg/dns/edns_test.go
@@ -0,0 +1,68 @@
+package dns
+
+import "testing"
+
+func TestOPTTtl(t *testing.T) {
+ e := &OPT{}
+ e.Hdr.Name = "."
+ e.Hdr.Rrtype = TypeOPT
+
+ // verify the default setting of DO=0
+ if e.Do() {
+ t.Errorf("DO bit should be zero")
+ }
+
+ // There are 6 possible invocations of SetDo():
+ //
+ // 1. Starting with DO=0, using SetDo()
+ // 2. Starting with DO=0, using SetDo(true)
+ // 3. Starting with DO=0, using SetDo(false)
+ // 4. Starting with DO=1, using SetDo()
+ // 5. Starting with DO=1, using SetDo(true)
+ // 6. Starting with DO=1, using SetDo(false)
+
+ // verify that invoking SetDo() sets DO=1 (TEST #1)
+ e.SetDo()
+ if !e.Do() {
+ t.Errorf("DO bit should be non-zero")
+ }
+ // verify that using SetDo(true) works when DO=1 (TEST #5)
+ e.SetDo(true)
+ if !e.Do() {
+ t.Errorf("DO bit should still be non-zero")
+ }
+ // verify that we can use SetDo(false) to set DO=0 (TEST #6)
+ e.SetDo(false)
+ if e.Do() {
+ t.Errorf("DO bit should be zero")
+ }
+ // verify that if we call SetDo(false) when DO=0 that it is unchanged (TEST #3)
+ e.SetDo(false)
+ if e.Do() {
+ t.Errorf("DO bit should still be zero")
+ }
+ // verify that using SetDo(true) works for DO=0 (TEST #2)
+ e.SetDo(true)
+ if !e.Do() {
+ t.Errorf("DO bit should be non-zero")
+ }
+ // verify that using SetDo() works for DO=1 (TEST #4)
+ e.SetDo()
+ if !e.Do() {
+ t.Errorf("DO bit should be non-zero")
+ }
+
+ if e.Version() != 0 {
+ t.Errorf("version should be non-zero")
+ }
+
+ e.SetVersion(42)
+ if e.Version() != 42 {
+ t.Errorf("set 42, expected %d, got %d", 42, e.Version())
+ }
+
+ e.SetExtendedRcode(42)
+ if e.ExtendedRcode() != 42 {
+ t.Errorf("set 42, expected %d, got %d", 42-15, e.ExtendedRcode())
+ }
+}
diff --git a/vendor/github.com/miekg/dns/example_test.go b/vendor/github.com/miekg/dns/example_test.go
new file mode 100644
index 0000000000..64c14962cd
--- /dev/null
+++ b/vendor/github.com/miekg/dns/example_test.go
@@ -0,0 +1,146 @@
+package dns_test
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ "net"
+
+ "github.com/miekg/dns"
+)
+
+// Retrieve the MX records for miek.nl.
+func ExampleMX() {
+ config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
+ c := new(dns.Client)
+ m := new(dns.Msg)
+ m.SetQuestion("miek.nl.", dns.TypeMX)
+ m.RecursionDesired = true
+ r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
+ if err != nil {
+ return
+ }
+ if r.Rcode != dns.RcodeSuccess {
+ return
+ }
+ for _, a := range r.Answer {
+ if mx, ok := a.(*dns.MX); ok {
+ fmt.Printf("%s\n", mx.String())
+ }
+ }
+}
+
+// Retrieve the DNSKEY records of a zone and convert them
+// to DS records for SHA1, SHA256 and SHA384.
+func ExampleDS() {
+ config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
+ c := new(dns.Client)
+ m := new(dns.Msg)
+ zone := "miek.nl"
+ m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
+ m.SetEdns0(4096, true)
+ r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
+ if err != nil {
+ return
+ }
+ if r.Rcode != dns.RcodeSuccess {
+ return
+ }
+ for _, k := range r.Answer {
+ if key, ok := k.(*dns.DNSKEY); ok {
+ for _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} {
+ fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags)
+ }
+ }
+ }
+}
+
+const TypeAPAIR = 0x0F99
+
+type APAIR struct {
+ addr [2]net.IP
+}
+
+func NewAPAIR() dns.PrivateRdata { return new(APAIR) }
+
+func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() }
+func (rd *APAIR) Parse(txt []string) error {
+ if len(txt) != 2 {
+ return errors.New("two addresses required for APAIR")
+ }
+ for i, s := range txt {
+ ip := net.ParseIP(s)
+ if ip == nil {
+ return errors.New("invalid IP in APAIR text representation")
+ }
+ rd.addr[i] = ip
+ }
+ return nil
+}
+
+func (rd *APAIR) Pack(buf []byte) (int, error) {
+ b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)
+ n := copy(buf, b)
+ if n != len(b) {
+ return n, dns.ErrBuf
+ }
+ return n, nil
+}
+
+func (rd *APAIR) Unpack(buf []byte) (int, error) {
+ ln := net.IPv4len * 2
+ if len(buf) != ln {
+ return 0, errors.New("invalid length of APAIR rdata")
+ }
+ cp := make([]byte, ln)
+ copy(cp, buf) // clone bytes to use them in IPs
+
+ rd.addr[0] = net.IP(cp[:3])
+ rd.addr[1] = net.IP(cp[4:])
+
+ return len(buf), nil
+}
+
+func (rd *APAIR) Copy(dest dns.PrivateRdata) error {
+ cp := make([]byte, rd.Len())
+ _, err := rd.Pack(cp)
+ if err != nil {
+ return err
+ }
+
+ d := dest.(*APAIR)
+ d.addr[0] = net.IP(cp[:3])
+ d.addr[1] = net.IP(cp[4:])
+ return nil
+}
+
+func (rd *APAIR) Len() int {
+ return net.IPv4len * 2
+}
+
+func ExamplePrivateHandle() {
+ dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR)
+ defer dns.PrivateHandleRemove(TypeAPAIR)
+
+ rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)")
+ if err != nil {
+ log.Fatal("could not parse APAIR record: ", err)
+ }
+ fmt.Println(rr)
+ // Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
+
+ m := new(dns.Msg)
+ m.Id = 12345
+ m.SetQuestion("miek.nl.", TypeAPAIR)
+ m.Answer = append(m.Answer, rr)
+
+ fmt.Println(m)
+ // ;; opcode: QUERY, status: NOERROR, id: 12345
+ // ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+ //
+ // ;; QUESTION SECTION:
+ // ;miek.nl. IN APAIR
+ //
+ // ;; ANSWER SECTION:
+ // miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
+}
diff --git a/vendor/github.com/miekg/dns/format.go b/vendor/github.com/miekg/dns/format.go
new file mode 100644
index 0000000000..3f5303c201
--- /dev/null
+++ b/vendor/github.com/miekg/dns/format.go
@@ -0,0 +1,87 @@
+package dns
+
+import (
+ "net"
+ "reflect"
+ "strconv"
+)
+
+// NumField returns the number of rdata fields r has.
+func NumField(r RR) int {
+ return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
+}
+
+// Field returns the rdata field i as a string. Fields are indexed starting from 1.
+// RR types that holds slice data, for instance the NSEC type bitmap will return a single
+// string where the types are concatenated using a space.
+// Accessing non existing fields will cause a panic.
+func Field(r RR, i int) string {
+ if i == 0 {
+ return ""
+ }
+ d := reflect.ValueOf(r).Elem().Field(i)
+ switch 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
+ default:
+ // if it does not have a tag its a string slice
+ fallthrough
+ case `dns:"txt"`:
+ if d.Len() == 0 {
+ return ""
+ }
+ s := d.Index(0).String()
+ for i := 1; i < d.Len(); i++ {
+ s += " " + d.Index(i).String()
+ }
+ return s
+ }
+ }
+ return ""
+}
diff --git a/vendor/github.com/miekg/dns/fuzz_test.go b/vendor/github.com/miekg/dns/fuzz_test.go
new file mode 100644
index 0000000000..255869730e
--- /dev/null
+++ b/vendor/github.com/miekg/dns/fuzz_test.go
@@ -0,0 +1,25 @@
+package dns
+
+import "testing"
+
+func TestFuzzString(t *testing.T) {
+ testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
+ ";a ", ";a����������",
+ " NSAP O ", " NSAP N ",
+ " TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
+ " TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
+ "$GENERATE 0-3 ${441189,5039418474430,o}",
+ "$INCLUDE 00 TYPE00000000000n ",
+ "$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
+ }
+ for i, tc := range testcases {
+ rr, err := NewRR(tc)
+ if err == nil {
+ // rr can be nil because we can (for instance) just parse a comment
+ if rr == nil {
+ continue
+ }
+ t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
+ }
+ }
+}
diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go
new file mode 100644
index 0000000000..e4481a4b0d
--- /dev/null
+++ b/vendor/github.com/miekg/dns/generate.go
@@ -0,0 +1,159 @@
+package dns
+
+import (
+ "bytes"
+ "errors"
+ "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* occurrences
+// 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, err := strconv.Atoi(l.token[i+1:]); err == 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 error
+ 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 != nil {
+ return err.Error()
+ }
+ 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, err := NewRR("$ORIGIN " + o + "\n" + dom.String())
+ if err != nil {
+ return err.Error()
+ }
+ 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, error) {
+ xs := strings.SplitN(s, ",", 3)
+ if len(xs) != 3 {
+ return "", 0, errors.New("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, errors.New("bad base in $GENERATE")
+ }
+ offset, err := strconv.Atoi(xs[0])
+ if err != nil || offset > 255 {
+ return "", 0, errors.New("bad offset in $GENERATE")
+ }
+ width, err := strconv.Atoi(xs[1])
+ if err != nil || width > 255 {
+ return "", offset, errors.New("bad width in $GENERATE")
+ }
+ switch {
+ case width < 0:
+ return "", offset, errors.New("bad width in $GENERATE")
+ case width == 0:
+ return "%" + xs[1] + xs[2], offset, nil
+ }
+ return "%0" + xs[1] + xs[2], offset, nil
+}
diff --git a/vendor/github.com/miekg/dns/idn/code_points.go b/vendor/github.com/miekg/dns/idn/code_points.go
new file mode 100644
index 0000000000..129c3742f5
--- /dev/null
+++ b/vendor/github.com/miekg/dns/idn/code_points.go
@@ -0,0 +1,2346 @@
+package idn
+
+const (
+ propertyUnknown property = iota // unknown character property
+ propertyPVALID // allowed to be used in IDNs
+ propertyCONTEXTJ // invisible or problematic characters (join controls)
+ propertyCONTEXTO // invisible or problematic characters (others)
+ propertyDISALLOWED // should not be included in IDNs
+ propertyUNASSIGNED // code points that are not designated in the Unicode Standard
+)
+
+// property stores the property of a code point, as described in RFC 5892,
+// section 1
+type property int
+
+// codePoints list all code points in Unicode Character Database (UCD) Format
+// according to RFC 5892, appendix B.1. Thanks to libidn2 (GNU) -
+// http://www.gnu.org/software/libidn/libidn2/
+var codePoints = []struct {
+ start rune
+ end rune
+ state property
+}{
+ {0x0000, 0x002C, propertyDISALLOWED}, // ..COMMA
+ {0x002D, 0x0, propertyPVALID}, // HYPHEN-MINUS
+ {0x002E, 0x002F, propertyDISALLOWED}, // FULL STOP..SOLIDUS
+ {0x0030, 0x0039, propertyPVALID}, // DIGIT ZERO..DIGIT NINE
+ {0x003A, 0x0060, propertyDISALLOWED}, // COLON..GRAVE ACCENT
+ {0x0041, 0x005A, propertyPVALID}, // LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+ {0x0061, 0x007A, propertyPVALID}, // LATIN SMALL LETTER A..LATIN SMALL LETTER Z
+ {0x007B, 0x00B6, propertyDISALLOWED}, // LEFT CURLY BRACKET..PILCROW SIGN
+ {0x00B7, 0x0, propertyCONTEXTO}, // MIDDLE DOT
+ {0x00B8, 0x00DE, propertyDISALLOWED}, // CEDILLA..LATIN CAPITAL LETTER THORN
+ {0x00DF, 0x00F6, propertyPVALID}, // LATIN SMALL LETTER SHARP S..LATIN SMALL LETT
+ {0x00F7, 0x0, propertyDISALLOWED}, // DIVISION SIGN
+ {0x00F8, 0x00FF, propertyPVALID}, // LATIN SMALL LETTER O WITH STROKE..LATIN SMAL
+ {0x0100, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH MACRON
+ {0x0101, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH MACRON
+ {0x0102, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH BREVE
+ {0x0103, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH BREVE
+ {0x0104, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH OGONEK
+ {0x0105, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH OGONEK
+ {0x0106, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH ACUTE
+ {0x0107, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH ACUTE
+ {0x0108, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+ {0x0109, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH CIRCUMFLEX
+ {0x010A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH DOT ABOVE
+ {0x010B, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH DOT ABOVE
+ {0x010C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH CARON
+ {0x010D, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH CARON
+ {0x010E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER D WITH CARON
+ {0x010F, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH CARON
+ {0x0110, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER D WITH STROKE
+ {0x0111, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH STROKE
+ {0x0112, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH MACRON
+ {0x0113, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH MACRON
+ {0x0114, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH BREVE
+ {0x0115, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH BREVE
+ {0x0116, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH DOT ABOVE
+ {0x0117, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH DOT ABOVE
+ {0x0118, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH OGONEK
+ {0x0119, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH OGONEK
+ {0x011A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH CARON
+ {0x011B, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH CARON
+ {0x011C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+ {0x011D, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CIRCUMFLEX
+ {0x011E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH BREVE
+ {0x011F, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH BREVE
+ {0x0120, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH DOT ABOVE
+ {0x0121, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH DOT ABOVE
+ {0x0122, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CEDILLA
+ {0x0123, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CEDILLA
+ {0x0124, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+ {0x0125, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH CIRCUMFLEX
+ {0x0126, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH STROKE
+ {0x0127, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH STROKE
+ {0x0128, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH TILDE
+ {0x0129, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH TILDE
+ {0x012A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH MACRON
+ {0x012B, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH MACRON
+ {0x012C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH BREVE
+ {0x012D, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH BREVE
+ {0x012E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH OGONEK
+ {0x012F, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH OGONEK
+ {0x0130, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH DOT ABOVE
+ {0x0131, 0x0, propertyPVALID}, // LATIN SMALL LETTER DOTLESS I
+ {0x0132, 0x0134, propertyDISALLOWED}, // LATIN CAPITAL LIGATURE IJ..LATIN CAPITAL LET
+ {0x0135, 0x0, propertyPVALID}, // LATIN SMALL LETTER J WITH CIRCUMFLEX
+ {0x0136, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER K WITH CEDILLA
+ {0x0137, 0x0138, propertyPVALID}, // LATIN SMALL LETTER K WITH CEDILLA..LATIN SMA
+ {0x0139, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH ACUTE
+ {0x013A, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH ACUTE
+ {0x013B, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH CEDILLA
+ {0x013C, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH CEDILLA
+ {0x013D, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH CARON
+ {0x013E, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH CARON
+ {0x013F, 0x0141, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATI
+ {0x0142, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH STROKE
+ {0x0143, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH ACUTE
+ {0x0144, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH ACUTE
+ {0x0145, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH CEDILLA
+ {0x0146, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH CEDILLA
+ {0x0147, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH CARON
+ {0x0148, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH CARON
+ {0x0149, 0x014A, propertyDISALLOWED}, // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE.
+ {0x014B, 0x0, propertyPVALID}, // LATIN SMALL LETTER ENG
+ {0x014C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH MACRON
+ {0x014D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH MACRON
+ {0x014E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH BREVE
+ {0x014F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH BREVE
+ {0x0150, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+ {0x0151, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ {0x0152, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LIGATURE OE
+ {0x0153, 0x0, propertyPVALID}, // LATIN SMALL LIGATURE OE
+ {0x0154, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH ACUTE
+ {0x0155, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH ACUTE
+ {0x0156, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH CEDILLA
+ {0x0157, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH CEDILLA
+ {0x0158, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH CARON
+ {0x0159, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH CARON
+ {0x015A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH ACUTE
+ {0x015B, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH ACUTE
+ {0x015C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+ {0x015D, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CIRCUMFLEX
+ {0x015E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CEDILLA
+ {0x015F, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CEDILLA
+ {0x0160, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CARON
+ {0x0161, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CARON
+ {0x0162, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH CEDILLA
+ {0x0163, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH CEDILLA
+ {0x0164, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH CARON
+ {0x0165, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH CARON
+ {0x0166, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH STROKE
+ {0x0167, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH STROKE
+ {0x0168, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH TILDE
+ {0x0169, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH TILDE
+ {0x016A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH MACRON
+ {0x016B, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH MACRON
+ {0x016C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH BREVE
+ {0x016D, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH BREVE
+ {0x016E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH RING ABOVE
+ {0x016F, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH RING ABOVE
+ {0x0170, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+ {0x0171, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ {0x0172, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH OGONEK
+ {0x0173, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH OGONEK
+ {0x0174, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+ {0x0175, 0x0, propertyPVALID}, // LATIN SMALL LETTER W WITH CIRCUMFLEX
+ {0x0176, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+ {0x0177, 0x0, propertyPVALID}, // LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ {0x0178, 0x0179, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH DIAERESIS..LATIN
+ {0x017A, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH ACUTE
+ {0x017B, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH DOT ABOVE
+ {0x017C, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH DOT ABOVE
+ {0x017D, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH CARON
+ {0x017E, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH CARON
+ {0x017F, 0x0, propertyDISALLOWED}, // LATIN SMALL LETTER LONG S
+ {0x0180, 0x0, propertyPVALID}, // LATIN SMALL LETTER B WITH STROKE
+ {0x0181, 0x0182, propertyDISALLOWED}, // LATIN CAPITAL LETTER B WITH HOOK..LATIN CAPI
+ {0x0183, 0x0, propertyPVALID}, // LATIN SMALL LETTER B WITH TOPBAR
+ {0x0184, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER TONE SIX
+ {0x0185, 0x0, propertyPVALID}, // LATIN SMALL LETTER TONE SIX
+ {0x0186, 0x0187, propertyDISALLOWED}, // LATIN CAPITAL LETTER OPEN O..LATIN CAPITAL L
+ {0x0188, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH HOOK
+ {0x0189, 0x018B, propertyDISALLOWED}, // LATIN CAPITAL LETTER AFRICAN D..LATIN CAPITA
+ {0x018C, 0x018D, propertyPVALID}, // LATIN SMALL LETTER D WITH TOPBAR..LATIN SMAL
+ {0x018E, 0x0191, propertyDISALLOWED}, // LATIN CAPITAL LETTER REVERSED E..LATIN CAPIT
+ {0x0192, 0x0, propertyPVALID}, // LATIN SMALL LETTER F WITH HOOK
+ {0x0193, 0x0194, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH HOOK..LATIN CAPI
+ {0x0195, 0x0, propertyPVALID}, // LATIN SMALL LETTER HV
+ {0x0196, 0x0198, propertyDISALLOWED}, // LATIN CAPITAL LETTER IOTA..LATIN CAPITAL LET
+ {0x0199, 0x019B, propertyPVALID}, // LATIN SMALL LETTER K WITH HOOK..LATIN SMALL
+ {0x019C, 0x019D, propertyDISALLOWED}, // LATIN CAPITAL LETTER TURNED M..LATIN CAPITAL
+ {0x019E, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH LONG RIGHT LEG
+ {0x019F, 0x01A0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH MIDDLE TILDE..LA
+ {0x01A1, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH HORN
+ {0x01A2, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER OI
+ {0x01A3, 0x0, propertyPVALID}, // LATIN SMALL LETTER OI
+ {0x01A4, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER P WITH HOOK
+ {0x01A5, 0x0, propertyPVALID}, // LATIN SMALL LETTER P WITH HOOK
+ {0x01A6, 0x01A7, propertyDISALLOWED}, // LATIN LETTER YR..LATIN CAPITAL LETTER TONE T
+ {0x01A8, 0x0, propertyPVALID}, // LATIN SMALL LETTER TONE TWO
+ {0x01A9, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER ESH
+ {0x01AA, 0x01AB, propertyPVALID}, // LATIN LETTER REVERSED ESH LOOP..LATIN SMALL
+ {0x01AC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH HOOK
+ {0x01AD, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH HOOK
+ {0x01AE, 0x01AF, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK..
+ {0x01B0, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH HORN
+ {0x01B1, 0x01B3, propertyDISALLOWED}, // LATIN CAPITAL LETTER UPSILON..LATIN CAPITAL
+ {0x01B4, 0x0, propertyPVALID}, // LATIN SMALL LETTER Y WITH HOOK
+ {0x01B5, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH STROKE
+ {0x01B6, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH STROKE
+ {0x01B7, 0x01B8, propertyDISALLOWED}, // LATIN CAPITAL LETTER EZH..LATIN CAPITAL LETT
+ {0x01B9, 0x01BB, propertyPVALID}, // LATIN SMALL LETTER EZH REVERSED..LATIN LETTE
+ {0x01BC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER TONE FIVE
+ {0x01BD, 0x01C3, propertyPVALID}, // LATIN SMALL LETTER TONE FIVE..LATIN LETTER R
+ {0x01C4, 0x01CD, propertyDISALLOWED}, // LATIN CAPITAL LETTER DZ WITH CARON..LATIN CA
+ {0x01CE, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH CARON
+ {0x01CF, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH CARON
+ {0x01D0, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH CARON
+ {0x01D1, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH CARON
+ {0x01D2, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH CARON
+ {0x01D3, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH CARON
+ {0x01D4, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH CARON
+ {0x01D5, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND MA
+ {0x01D6, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND MACR
+ {0x01D7, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND AC
+ {0x01D8, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND ACUT
+ {0x01D9, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND CA
+ {0x01DA, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND CARO
+ {0x01DB, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND GR
+ {0x01DC, 0x01DD, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND GRAV
+ {0x01DE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DIAERESIS AND MA
+ {0x01DF, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DIAERESIS AND MACR
+ {0x01E0, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOT ABOVE AND MA
+ {0x01E1, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOT ABOVE AND MACR
+ {0x01E2, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER AE WITH MACRON
+ {0x01E3, 0x0, propertyPVALID}, // LATIN SMALL LETTER AE WITH MACRON
+ {0x01E4, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH STROKE
+ {0x01E5, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH STROKE
+ {0x01E6, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CARON
+ {0x01E7, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CARON
+ {0x01E8, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER K WITH CARON
+ {0x01E9, 0x0, propertyPVALID}, // LATIN SMALL LETTER K WITH CARON
+ {0x01EA, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH OGONEK
+ {0x01EB, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH OGONEK
+ {0x01EC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH OGONEK AND MACRO
+ {0x01ED, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH OGONEK AND MACRON
+ {0x01EE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER EZH WITH CARON
+ {0x01EF, 0x01F0, propertyPVALID}, // LATIN SMALL LETTER EZH WITH CARON..LATIN SMA
+ {0x01F1, 0x01F4, propertyDISALLOWED}, // LATIN CAPITAL LETTER DZ..LATIN CAPITAL LETTE
+ {0x01F5, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH ACUTE
+ {0x01F6, 0x01F8, propertyDISALLOWED}, // LATIN CAPITAL LETTER HWAIR..LATIN CAPITAL LE
+ {0x01F9, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH GRAVE
+ {0x01FA, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH RING ABOVE AND A
+ {0x01FB, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH RING ABOVE AND ACU
+ {0x01FC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER AE WITH ACUTE
+ {0x01FD, 0x0, propertyPVALID}, // LATIN SMALL LETTER AE WITH ACUTE
+ {0x01FE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+ {0x01FF, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH STROKE AND ACUTE
+ {0x0200, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+ {0x0201, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOUBLE GRAVE
+ {0x0202, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH INVERTED BREVE
+ {0x0203, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH INVERTED BREVE
+ {0x0204, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+ {0x0205, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH DOUBLE GRAVE
+ {0x0206, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH INVERTED BREVE
+ {0x0207, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH INVERTED BREVE
+ {0x0208, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+ {0x0209, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH DOUBLE GRAVE
+ {0x020A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH INVERTED BREVE
+ {0x020B, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH INVERTED BREVE
+ {0x020C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+ {0x020D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOUBLE GRAVE
+ {0x020E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH INVERTED BREVE
+ {0x020F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH INVERTED BREVE
+ {0x0210, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+ {0x0211, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH DOUBLE GRAVE
+ {0x0212, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH INVERTED BREVE
+ {0x0213, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH INVERTED BREVE
+ {0x0214, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+ {0x0215, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DOUBLE GRAVE
+ {0x0216, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH INVERTED BREVE
+ {0x0217, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH INVERTED BREVE
+ {0x0218, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH COMMA BELOW
+ {0x0219, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH COMMA BELOW
+ {0x021A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH COMMA BELOW
+ {0x021B, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH COMMA BELOW
+ {0x021C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER YOGH
+ {0x021D, 0x0, propertyPVALID}, // LATIN SMALL LETTER YOGH
+ {0x021E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH CARON
+ {0x021F, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH CARON
+ {0x0220, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
+ {0x0221, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH CURL
+ {0x0222, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER OU
+ {0x0223, 0x0, propertyPVALID}, // LATIN SMALL LETTER OU
+ {0x0224, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH HOOK
+ {0x0225, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH HOOK
+ {0x0226, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOT ABOVE
+ {0x0227, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOT ABOVE
+ {0x0228, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH CEDILLA
+ {0x0229, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH CEDILLA
+ {0x022A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DIAERESIS AND MA
+ {0x022B, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DIAERESIS AND MACR
+ {0x022C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+ {0x022D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH TILDE AND MACRON
+ {0x022E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOT ABOVE
+ {0x022F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOT ABOVE
+ {0x0230, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOT ABOVE AND MA
+ {0x0231, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOT ABOVE AND MACR
+ {0x0232, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH MACRON
+ {0x0233, 0x0239, propertyPVALID}, // LATIN SMALL LETTER Y WITH MACRON..LATIN SMAL
+ {0x023A, 0x023B, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH STROKE..LATIN CA
+ {0x023C, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH STROKE
+ {0x023D, 0x023E, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH BAR..LATIN CAPIT
+ {0x023F, 0x0240, propertyPVALID}, // LATIN SMALL LETTER S WITH SWASH TAIL..LATIN
+ {0x0241, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER GLOTTAL STOP
+ {0x0242, 0x0, propertyPVALID}, // LATIN SMALL LETTER GLOTTAL STOP
+ {0x0243, 0x0246, propertyDISALLOWED}, // LATIN CAPITAL LETTER B WITH STROKE..LATIN CA
+ {0x0247, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH STROKE
+ {0x0248, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER J WITH STROKE
+ {0x0249, 0x0, propertyPVALID}, // LATIN SMALL LETTER J WITH STROKE
+ {0x024A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL
+ {0x024B, 0x0, propertyPVALID}, // LATIN SMALL LETTER Q WITH HOOK TAIL
+ {0x024C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH STROKE
+ {0x024D, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH STROKE
+ {0x024E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH STROKE
+ {0x024F, 0x02AF, propertyPVALID}, // LATIN SMALL LETTER Y WITH STROKE..LATIN SMAL
+ {0x02B0, 0x02B8, propertyDISALLOWED}, // MODIFIER LETTER SMALL H..MODIFIER LETTER SMA
+ {0x02B9, 0x02C1, propertyPVALID}, // MODIFIER LETTER PRIME..MODIFIER LETTER REVER
+ {0x02C2, 0x02C5, propertyDISALLOWED}, // MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LET
+ {0x02C6, 0x02D1, propertyPVALID}, // MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER
+ {0x02D2, 0x02EB, propertyDISALLOWED}, // MODIFIER LETTER CENTRED RIGHT HALF RING..MOD
+ {0x02EC, 0x0, propertyPVALID}, // MODIFIER LETTER VOICING
+ {0x02ED, 0x0, propertyDISALLOWED}, // MODIFIER LETTER UNASPIRATED
+ {0x02EE, 0x0, propertyPVALID}, // MODIFIER LETTER DOUBLE APOSTROPHE
+ {0x02EF, 0x02FF, propertyDISALLOWED}, // MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER
+ {0x0300, 0x033F, propertyPVALID}, // COMBINING GRAVE ACCENT..COMBINING DOUBLE OVE
+ {0x0340, 0x0341, propertyDISALLOWED}, // COMBINING GRAVE TONE MARK..COMBINING ACUTE T
+ {0x0342, 0x0, propertyPVALID}, // COMBINING GREEK PERISPOMENI
+ {0x0343, 0x0345, propertyDISALLOWED}, // COMBINING GREEK KORONIS..COMBINING GREEK YPO
+ {0x0346, 0x034E, propertyPVALID}, // COMBINING BRIDGE ABOVE..COMBINING UPWARDS AR
+ {0x034F, 0x0, propertyDISALLOWED}, // COMBINING GRAPHEME JOINER
+ {0x0350, 0x036F, propertyPVALID}, // COMBINING RIGHT ARROWHEAD ABOVE..COMBINING L
+ {0x0370, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER HETA
+ {0x0371, 0x0, propertyPVALID}, // GREEK SMALL LETTER HETA
+ {0x0372, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER ARCHAIC SAMPI
+ {0x0373, 0x0, propertyPVALID}, // GREEK SMALL LETTER ARCHAIC SAMPI
+ {0x0374, 0x0, propertyDISALLOWED}, // GREEK NUMERAL SIGN
+ {0x0375, 0x0, propertyCONTEXTO}, // GREEK LOWER NUMERAL SIGN
+ {0x0376, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
+ {0x0377, 0x0, propertyPVALID}, // GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
+ {0x0378, 0x0379, propertyUNASSIGNED}, // ..
+ {0x037A, 0x0, propertyDISALLOWED}, // GREEK YPOGEGRAMMENI
+ {0x037B, 0x037D, propertyPVALID}, // GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GR
+ {0x037E, 0x0, propertyDISALLOWED}, // GREEK QUESTION MARK
+ {0x037F, 0x0383, propertyUNASSIGNED}, // ..
+ {0x0384, 0x038A, propertyDISALLOWED}, // GREEK TONOS..GREEK CAPITAL LETTER IOTA WITH
+ {0x038B, 0x0, propertyUNASSIGNED}, //
+ {0x038C, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER OMICRON WITH TONOS
+ {0x038D, 0x0, propertyUNASSIGNED}, //
+ {0x038E, 0x038F, propertyDISALLOWED}, // GREEK CAPITAL LETTER UPSILON WITH TONOS..GRE
+ {0x0390, 0x0, propertyPVALID}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND T
+ {0x0391, 0x03A1, propertyDISALLOWED}, // GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LE
+ {0x03A2, 0x0, propertyUNASSIGNED}, //
+ {0x03A3, 0x03AB, propertyDISALLOWED}, // GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LE
+ {0x03AC, 0x03CE, propertyPVALID}, // GREEK SMALL LETTER ALPHA WITH TONOS..GREEK S
+ {0x03CF, 0x03D6, propertyDISALLOWED}, // GREEK CAPITAL KAI SYMBOL..GREEK PI SYMBOL
+ {0x03D7, 0x0, propertyPVALID}, // GREEK KAI SYMBOL
+ {0x03D8, 0x0, propertyDISALLOWED}, // GREEK LETTER ARCHAIC KOPPA
+ {0x03D9, 0x0, propertyPVALID}, // GREEK SMALL LETTER ARCHAIC KOPPA
+ {0x03DA, 0x0, propertyDISALLOWED}, // GREEK LETTER STIGMA
+ {0x03DB, 0x0, propertyPVALID}, // GREEK SMALL LETTER STIGMA
+ {0x03DC, 0x0, propertyDISALLOWED}, // GREEK LETTER DIGAMMA
+ {0x03DD, 0x0, propertyPVALID}, // GREEK SMALL LETTER DIGAMMA
+ {0x03DE, 0x0, propertyDISALLOWED}, // GREEK LETTER KOPPA
+ {0x03DF, 0x0, propertyPVALID}, // GREEK SMALL LETTER KOPPA
+ {0x03E0, 0x0, propertyDISALLOWED}, // GREEK LETTER SAMPI
+ {0x03E1, 0x0, propertyPVALID}, // GREEK SMALL LETTER SAMPI
+ {0x03E2, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER SHEI
+ {0x03E3, 0x0, propertyPVALID}, // COPTIC SMALL LETTER SHEI
+ {0x03E4, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER FEI
+ {0x03E5, 0x0, propertyPVALID}, // COPTIC SMALL LETTER FEI
+ {0x03E6, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER KHEI
+ {0x03E7, 0x0, propertyPVALID}, // COPTIC SMALL LETTER KHEI
+ {0x03E8, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER HORI
+ {0x03E9, 0x0, propertyPVALID}, // COPTIC SMALL LETTER HORI
+ {0x03EA, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER GANGIA
+ {0x03EB, 0x0, propertyPVALID}, // COPTIC SMALL LETTER GANGIA
+ {0x03EC, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER SHIMA
+ {0x03ED, 0x0, propertyPVALID}, // COPTIC SMALL LETTER SHIMA
+ {0x03EE, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER DEI
+ {0x03EF, 0x0, propertyPVALID}, // COPTIC SMALL LETTER DEI
+ {0x03F0, 0x03F2, propertyDISALLOWED}, // GREEK KAPPA SYMBOL..GREEK LUNATE SIGMA SYMBO
+ {0x03F3, 0x0, propertyPVALID}, // GREEK LETTER YOT
+ {0x03F4, 0x03F7, propertyDISALLOWED}, // GREEK CAPITAL THETA SYMBOL..GREEK CAPITAL LE
+ {0x03F8, 0x0, propertyPVALID}, // GREEK SMALL LETTER SHO
+ {0x03F9, 0x03FA, propertyDISALLOWED}, // GREEK CAPITAL LUNATE SIGMA SYMBOL..GREEK CAP
+ {0x03FB, 0x03FC, propertyPVALID}, // GREEK SMALL LETTER SAN..GREEK RHO WITH STROK
+ {0x03FD, 0x042F, propertyDISALLOWED}, // GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL..
+ {0x0430, 0x045F, propertyPVALID}, // CYRILLIC SMALL LETTER A..CYRILLIC SMALL LETT
+ {0x0460, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OMEGA
+ {0x0461, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OMEGA
+ {0x0462, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YAT
+ {0x0463, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YAT
+ {0x0464, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED E
+ {0x0465, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED E
+ {0x0466, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER LITTLE YUS
+ {0x0467, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER LITTLE YUS
+ {0x0468, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
+ {0x0469, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS
+ {0x046A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BIG YUS
+ {0x046B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BIG YUS
+ {0x046C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
+ {0x046D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED BIG YUS
+ {0x046E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KSI
+ {0x046F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KSI
+ {0x0470, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PSI
+ {0x0471, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PSI
+ {0x0472, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER FITA
+ {0x0473, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER FITA
+ {0x0474, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IZHITSA
+ {0x0475, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IZHITSA
+ {0x0476, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE
+ {0x0477, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GR
+ {0x0478, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER UK
+ {0x0479, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER UK
+ {0x047A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ROUND OMEGA
+ {0x047B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ROUND OMEGA
+ {0x047C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
+ {0x047D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OMEGA WITH TITLO
+ {0x047E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OT
+ {0x047F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OT
+ {0x0480, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOPPA
+ {0x0481, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOPPA
+ {0x0482, 0x0, propertyDISALLOWED}, // CYRILLIC THOUSANDS SIGN
+ {0x0483, 0x0487, propertyPVALID}, // COMBINING CYRILLIC TITLO..COMBINING CYRILLIC
+ {0x0488, 0x048A, propertyDISALLOWED}, // COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..C
+ {0x048B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SHORT I WITH TAIL
+ {0x048C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SEMISOFT SIGN
+ {0x048D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SEMISOFT SIGN
+ {0x048E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ER WITH TICK
+ {0x048F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ER WITH TICK
+ {0x0490, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ {0x0491, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH UPTURN
+ {0x0492, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH STROKE
+ {0x0493, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH STROKE
+ {0x0494, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
+ {0x0495, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK
+ {0x0496, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+ {0x0497, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH DESCENDER
+ {0x0498, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+ {0x0499, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZE WITH DESCENDER
+ {0x049A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+ {0x049B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH DESCENDER
+ {0x049C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH VERTICAL STR
+ {0x049D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH VERTICAL STROK
+ {0x049E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH STROKE
+ {0x049F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH STROKE
+ {0x04A0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BASHKIR KA
+ {0x04A1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BASHKIR KA
+ {0x04A2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+ {0x04A3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH DESCENDER
+ {0x04A4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE EN GHE
+ {0x04A5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE EN GHE
+ {0x04A6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
+ {0x04A7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK
+ {0x04A8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN HA
+ {0x04A9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN HA
+ {0x04AA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+ {0x04AB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ES WITH DESCENDER
+ {0x04AC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER TE WITH DESCENDER
+ {0x04AD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER TE WITH DESCENDER
+ {0x04AE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER STRAIGHT U
+ {0x04AF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER STRAIGHT U
+ {0x04B0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER STRAIGHT U WITH STRO
+ {0x04B1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
+ {0x04B2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+ {0x04B3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH DESCENDER
+ {0x04B4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE TE TSE
+ {0x04B5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE TE TSE
+ {0x04B6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+ {0x04B7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH DESCENDER
+ {0x04B8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH VERTICAL ST
+ {0x04B9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH VERTICAL STRO
+ {0x04BA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SHHA
+ {0x04BB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SHHA
+ {0x04BC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE
+ {0x04BD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN CHE
+ {0x04BE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH D
+ {0x04BF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DES
+ {0x04C0, 0x04C1, propertyDISALLOWED}, // CYRILLIC LETTER PALOCHKA..CYRILLIC CAPITAL L
+ {0x04C2, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH BREVE
+ {0x04C3, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH HOOK
+ {0x04C4, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH HOOK
+ {0x04C5, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH TAIL
+ {0x04C6, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH TAIL
+ {0x04C7, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH HOOK
+ {0x04C8, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH HOOK
+ {0x04C9, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH TAIL
+ {0x04CA, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH TAIL
+ {0x04CB, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
+ {0x04CC, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KHAKASSIAN CHE
+ {0x04CD, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EM WITH TAIL
+ {0x04CE, 0x04CF, propertyPVALID}, // CYRILLIC SMALL LETTER EM WITH TAIL..CYRILLIC
+ {0x04D0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER A WITH BREVE
+ {0x04D1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER A WITH BREVE
+ {0x04D2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+ {0x04D3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER A WITH DIAERESIS
+ {0x04D4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE A IE
+ {0x04D5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE A IE
+ {0x04D6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IE WITH BREVE
+ {0x04D7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IE WITH BREVE
+ {0x04D8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SCHWA
+ {0x04D9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SCHWA
+ {0x04DA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+ {0x04DB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
+ {0x04DC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+ {0x04DD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
+ {0x04DE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+ {0x04DF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+ {0x04E0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+ {0x04E1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN DZE
+ {0x04E2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER I WITH MACRON
+ {0x04E3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER I WITH MACRON
+ {0x04E4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+ {0x04E5, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER I WITH DIAERESIS
+ {0x04E6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+ {0x04E7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER O WITH DIAERESIS
+ {0x04E8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BARRED O
+ {0x04E9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BARRED O
+ {0x04EA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BARRED O WITH DIAERE
+ {0x04EB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BARRED O WITH DIAERESI
+ {0x04EC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+ {0x04ED, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER E WITH DIAERESIS
+ {0x04EE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH MACRON
+ {0x04EF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH MACRON
+ {0x04F0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+ {0x04F1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH DIAERESIS
+ {0x04F2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+ {0x04F3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
+ {0x04F4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+ {0x04F5, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+ {0x04F6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
+ {0x04F7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH DESCENDER
+ {0x04F8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+ {0x04F9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+ {0x04FA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH STROKE AND
+ {0x04FB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH STROKE AND HO
+ {0x04FC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH HOOK
+ {0x04FD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH HOOK
+ {0x04FE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH STROKE
+ {0x04FF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH STROKE
+ {0x0500, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DE
+ {0x0501, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DE
+ {0x0502, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DJE
+ {0x0503, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DJE
+ {0x0504, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI ZJE
+ {0x0505, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI ZJE
+ {0x0506, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DZJE
+ {0x0507, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DZJE
+ {0x0508, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI LJE
+ {0x0509, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI LJE
+ {0x050A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI NJE
+ {0x050B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI NJE
+ {0x050C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI SJE
+ {0x050D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI SJE
+ {0x050E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI TJE
+ {0x050F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI TJE
+ {0x0510, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER REVERSED ZE
+ {0x0511, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER REVERSED ZE
+ {0x0512, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH HOOK
+ {0x0513, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH HOOK
+ {0x0514, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER LHA
+ {0x0515, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER LHA
+ {0x0516, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER RHA
+ {0x0517, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER RHA
+ {0x0518, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YAE
+ {0x0519, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YAE
+ {0x051A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER QA
+ {0x051B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER QA
+ {0x051C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER WE
+ {0x051D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER WE
+ {0x051E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ALEUT KA
+ {0x051F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ALEUT KA
+ {0x0520, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK
+ {0x0521, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK
+ {0x0522, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK
+ {0x0523, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK
+ {0x0524, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PE WITH DESCENDER
+ {0x0525, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PE WITH DESCENDER
+ {0x0526, 0x0530, propertyUNASSIGNED}, // ..
+ {0x0531, 0x0556, propertyDISALLOWED}, // ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITA
+ {0x0557, 0x0558, propertyUNASSIGNED}, // ..
+ {0x0559, 0x0, propertyPVALID}, // ARMENIAN MODIFIER LETTER LEFT HALF RING
+ {0x055A, 0x055F, propertyDISALLOWED}, // ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION M
+ {0x0560, 0x0, propertyUNASSIGNED}, //
+ {0x0561, 0x0586, propertyPVALID}, // ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LE
+ {0x0587, 0x0, propertyDISALLOWED}, // ARMENIAN SMALL LIGATURE ECH YIWN
+ {0x0588, 0x0, propertyUNASSIGNED}, //
+ {0x0589, 0x058A, propertyDISALLOWED}, // ARMENIAN FULL STOP..ARMENIAN HYPHEN
+ {0x058B, 0x0590, propertyUNASSIGNED}, // ..
+ {0x0591, 0x05BD, propertyPVALID}, // HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+ {0x05BE, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION MAQAF
+ {0x05BF, 0x0, propertyPVALID}, // HEBREW POINT RAFE
+ {0x05C0, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION PASEQ
+ {0x05C1, 0x05C2, propertyPVALID}, // HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+ {0x05C3, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION SOF PASUQ
+ {0x05C4, 0x05C5, propertyPVALID}, // HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+ {0x05C6, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION NUN HAFUKHA
+ {0x05C7, 0x0, propertyPVALID}, // HEBREW POINT QAMATS QATAN
+ {0x05C8, 0x05CF, propertyUNASSIGNED}, // ..
+ {0x05D0, 0x05EA, propertyPVALID}, // HEBREW LETTER ALEF..HEBREW LETTER TAV
+ {0x05EB, 0x05EF, propertyUNASSIGNED}, // ..
+ {0x05F0, 0x05F2, propertyPVALID}, // HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW L
+ {0x05F3, 0x05F4, propertyCONTEXTO}, // HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATIO
+ {0x05F5, 0x05FF, propertyUNASSIGNED}, // ..
+ {0x0600, 0x0603, propertyDISALLOWED}, // ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
+ {0x0604, 0x0605, propertyUNASSIGNED}, // ..
+ {0x0606, 0x060F, propertyDISALLOWED}, // ARABIC-INDIC CUBE ROOT..ARABIC SIGN MISRA
+ {0x0610, 0x061A, propertyPVALID}, // ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..AR
+ {0x061B, 0x0, propertyDISALLOWED}, // ARABIC SEMICOLON
+ {0x061C, 0x061D, propertyUNASSIGNED}, // ..
+ {0x061E, 0x061F, propertyDISALLOWED}, // ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC Q
+ {0x0620, 0x0, propertyUNASSIGNED}, //
+ {0x0621, 0x063F, propertyPVALID}, // ARABIC LETTER HAMZA..ARABIC LETTER FARSI YEH
+ {0x0640, 0x0, propertyDISALLOWED}, // ARABIC TATWEEL
+ {0x0641, 0x065E, propertyPVALID}, // ARABIC LETTER FEH..ARABIC FATHA WITH TWO DOT
+ {0x065F, 0x0, propertyUNASSIGNED}, //
+ {0x0660, 0x0669, propertyCONTEXTO}, // ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT
+ {0x066A, 0x066D, propertyDISALLOWED}, // ARABIC PERCENT SIGN..ARABIC FIVE POINTED STA
+ {0x066E, 0x0674, propertyPVALID}, // ARABIC LETTER DOTLESS BEH..ARABIC LETTER HIG
+ {0x0675, 0x0678, propertyDISALLOWED}, // ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER
+ {0x0679, 0x06D3, propertyPVALID}, // ARABIC LETTER TTEH..ARABIC LETTER YEH BARREE
+ {0x06D4, 0x0, propertyDISALLOWED}, // ARABIC FULL STOP
+ {0x06D5, 0x06DC, propertyPVALID}, // ARABIC LETTER AE..ARABIC SMALL HIGH SEEN
+ {0x06DD, 0x06DE, propertyDISALLOWED}, // ARABIC END OF AYAH..ARABIC START OF RUB EL H
+ {0x06DF, 0x06E8, propertyPVALID}, // ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL
+ {0x06E9, 0x0, propertyDISALLOWED}, // ARABIC PLACE OF SAJDAH
+ {0x06EA, 0x06EF, propertyPVALID}, // ARABIC EMPTY CENTRE LOW STOP..ARABIC LETTER
+ {0x06F0, 0x06F9, propertyCONTEXTO}, // EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED A
+ {0x06FA, 0x06FF, propertyPVALID}, // ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC L
+ {0x0700, 0x070D, propertyDISALLOWED}, // SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN AST
+ {0x070E, 0x0, propertyUNASSIGNED}, //
+ {0x070F, 0x0, propertyDISALLOWED}, // SYRIAC ABBREVIATION MARK
+ {0x0710, 0x074A, propertyPVALID}, // SYRIAC LETTER ALAPH..SYRIAC BARREKH
+ {0x074B, 0x074C, propertyUNASSIGNED}, // ..
+ {0x074D, 0x07B1, propertyPVALID}, // SYRIAC LETTER SOGDIAN ZHAIN..THAANA LETTER N
+ {0x07B2, 0x07BF, propertyUNASSIGNED}, // ..
+ {0x07C0, 0x07F5, propertyPVALID}, // NKO DIGIT ZERO..NKO LOW TONE APOSTROPHE
+ {0x07F6, 0x07FA, propertyDISALLOWED}, // NKO SYMBOL OO DENNEN..NKO LAJANYALAN
+ {0x07FB, 0x07FF, propertyUNASSIGNED}, // ..
+ {0x0800, 0x082D, propertyPVALID}, // SAMARITAN LETTER ALAF..SAMARITAN MARK NEQUDA
+ {0x082E, 0x082F, propertyUNASSIGNED}, // ..
+ {0x0830, 0x083E, propertyDISALLOWED}, // SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUN
+ {0x083F, 0x08FF, propertyUNASSIGNED}, // ..
+ {0x0900, 0x0939, propertyPVALID}, // DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANA
+ {0x093A, 0x093B, propertyUNASSIGNED}, // ..
+ {0x093C, 0x094E, propertyPVALID}, // DEVANAGARI SIGN NUKTA..DEVANAGARI VOWEL SIGN
+ {0x094F, 0x0, propertyUNASSIGNED}, //
+ {0x0950, 0x0955, propertyPVALID}, // DEVANAGARI OM..DEVANAGARI VOWEL SIGN CANDRA
+ {0x0956, 0x0957, propertyUNASSIGNED}, // ..
+ {0x0958, 0x095F, propertyDISALLOWED}, // DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+ {0x0960, 0x0963, propertyPVALID}, // DEVANAGARI LETTER VOCALIC RR..DEVANAGARI VOW
+ {0x0964, 0x0965, propertyDISALLOWED}, // DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
+ {0x0966, 0x096F, propertyPVALID}, // DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
+ {0x0970, 0x0, propertyDISALLOWED}, // DEVANAGARI ABBREVIATION SIGN
+ {0x0971, 0x0972, propertyPVALID}, // DEVANAGARI SIGN HIGH SPACING DOT..DEVANAGARI
+ {0x0973, 0x0978, propertyUNASSIGNED}, // ..
+ {0x0979, 0x097F, propertyPVALID}, // DEVANAGARI LETTER ZHA..DEVANAGARI LETTER BBA
+ {0x0980, 0x0, propertyUNASSIGNED}, //
+ {0x0981, 0x0983, propertyPVALID}, // BENGALI SIGN CANDRABINDU..BENGALI SIGN VISAR
+ {0x0984, 0x0, propertyUNASSIGNED}, //
+ {0x0985, 0x098C, propertyPVALID}, // BENGALI LETTER A..BENGALI LETTER VOCALIC L
+ {0x098D, 0x098E, propertyUNASSIGNED}, // ..
+ {0x098F, 0x0990, propertyPVALID}, // BENGALI LETTER E..BENGALI LETTER AI
+ {0x0991, 0x0992, propertyUNASSIGNED}, // ..
+ {0x0993, 0x09A8, propertyPVALID}, // BENGALI LETTER O..BENGALI LETTER NA
+ {0x09A9, 0x0, propertyUNASSIGNED}, //
+ {0x09AA, 0x09B0, propertyPVALID}, // BENGALI LETTER PA..BENGALI LETTER RA
+ {0x09B1, 0x0, propertyUNASSIGNED}, //
+ {0x09B2, 0x0, propertyPVALID}, // BENGALI LETTER LA
+ {0x09B3, 0x09B5, propertyUNASSIGNED}, // ..
+ {0x09B6, 0x09B9, propertyPVALID}, // BENGALI LETTER SHA..BENGALI LETTER HA
+ {0x09BA, 0x09BB, propertyUNASSIGNED}, // ..
+ {0x09BC, 0x09C4, propertyPVALID}, // BENGALI SIGN NUKTA..BENGALI VOWEL SIGN VOCAL
+ {0x09C5, 0x09C6, propertyUNASSIGNED}, // ..
+ {0x09C7, 0x09C8, propertyPVALID}, // BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+ {0x09C9, 0x09CA, propertyUNASSIGNED}, // ..
+ {0x09CB, 0x09CE, propertyPVALID}, // BENGALI VOWEL SIGN O..BENGALI LETTER KHANDA
+ {0x09CF, 0x09D6, propertyUNASSIGNED}, // ..
+ {0x09D7, 0x0, propertyPVALID}, // BENGALI AU LENGTH MARK
+ {0x09D8, 0x09DB, propertyUNASSIGNED}, // ..
+ {0x09DC, 0x09DD, propertyDISALLOWED}, // BENGALI LETTER RRA..BENGALI LETTER RHA
+ {0x09DE, 0x0, propertyUNASSIGNED}, //
+ {0x09DF, 0x0, propertyDISALLOWED}, // BENGALI LETTER YYA
+ {0x09E0, 0x09E3, propertyPVALID}, // BENGALI LETTER VOCALIC RR..BENGALI VOWEL SIG
+ {0x09E4, 0x09E5, propertyUNASSIGNED}, // ..
+ {0x09E6, 0x09F1, propertyPVALID}, // BENGALI DIGIT ZERO..BENGALI LETTER RA WITH L
+ {0x09F2, 0x09FB, propertyDISALLOWED}, // BENGALI RUPEE MARK..BENGALI GANDA MARK
+ {0x09FC, 0x0A00, propertyUNASSIGNED}, // ..
+ {0x0A01, 0x0A03, propertyPVALID}, // GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN VISA
+ {0x0A04, 0x0, propertyUNASSIGNED}, //
+ {0x0A05, 0x0A0A, propertyPVALID}, // GURMUKHI LETTER A..GURMUKHI LETTER UU
+ {0x0A0B, 0x0A0E, propertyUNASSIGNED}, // ..
+ {0x0A0F, 0x0A10, propertyPVALID}, // GURMUKHI LETTER EE..GURMUKHI LETTER AI
+ {0x0A11, 0x0A12, propertyUNASSIGNED}, // ..
+ {0x0A13, 0x0A28, propertyPVALID}, // GURMUKHI LETTER OO..GURMUKHI LETTER NA
+ {0x0A29, 0x0, propertyUNASSIGNED}, //
+ {0x0A2A, 0x0A30, propertyPVALID}, // GURMUKHI LETTER PA..GURMUKHI LETTER RA
+ {0x0A31, 0x0, propertyUNASSIGNED}, //
+ {0x0A32, 0x0, propertyPVALID}, // GURMUKHI LETTER LA
+ {0x0A33, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER LLA
+ {0x0A34, 0x0, propertyUNASSIGNED}, //
+ {0x0A35, 0x0, propertyPVALID}, // GURMUKHI LETTER VA
+ {0x0A36, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER SHA
+ {0x0A37, 0x0, propertyUNASSIGNED}, //
+ {0x0A38, 0x0A39, propertyPVALID}, // GURMUKHI LETTER SA..GURMUKHI LETTER HA
+ {0x0A3A, 0x0A3B, propertyUNASSIGNED}, // ..
+ {0x0A3C, 0x0, propertyPVALID}, // GURMUKHI SIGN NUKTA
+ {0x0A3D, 0x0, propertyUNASSIGNED}, //
+ {0x0A3E, 0x0A42, propertyPVALID}, // GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN
+ {0x0A43, 0x0A46, propertyUNASSIGNED}, // ..
+ {0x0A47, 0x0A48, propertyPVALID}, // GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN
+ {0x0A49, 0x0A4A, propertyUNASSIGNED}, // ..
+ {0x0A4B, 0x0A4D, propertyPVALID}, // GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+ {0x0A4E, 0x0A50, propertyUNASSIGNED}, // ..
+ {0x0A51, 0x0, propertyPVALID}, // GURMUKHI SIGN UDAAT
+ {0x0A52, 0x0A58, propertyUNASSIGNED}, // ..
+ {0x0A59, 0x0A5B, propertyDISALLOWED}, // GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+ {0x0A5C, 0x0, propertyPVALID}, // GURMUKHI LETTER RRA
+ {0x0A5D, 0x0, propertyUNASSIGNED}, //
+ {0x0A5E, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER FA
+ {0x0A5F, 0x0A65, propertyUNASSIGNED}, // ..
+ {0x0A66, 0x0A75, propertyPVALID}, // GURMUKHI DIGIT ZERO..GURMUKHI SIGN YAKASH
+ {0x0A76, 0x0A80, propertyUNASSIGNED}, // ..
+ {0x0A81, 0x0A83, propertyPVALID}, // GUJARATI SIGN CANDRABINDU..GUJARATI SIGN VIS
+ {0x0A84, 0x0, propertyUNASSIGNED}, //
+ {0x0A85, 0x0A8D, propertyPVALID}, // GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
+ {0x0A8E, 0x0, propertyUNASSIGNED}, //
+ {0x0A8F, 0x0A91, propertyPVALID}, // GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
+ {0x0A92, 0x0, propertyUNASSIGNED}, //
+ {0x0A93, 0x0AA8, propertyPVALID}, // GUJARATI LETTER O..GUJARATI LETTER NA
+ {0x0AA9, 0x0, propertyUNASSIGNED}, //
+ {0x0AAA, 0x0AB0, propertyPVALID}, // GUJARATI LETTER PA..GUJARATI LETTER RA
+ {0x0AB1, 0x0, propertyUNASSIGNED}, //
+ {0x0AB2, 0x0AB3, propertyPVALID}, // GUJARATI LETTER LA..GUJARATI LETTER LLA
+ {0x0AB4, 0x0, propertyUNASSIGNED}, //
+ {0x0AB5, 0x0AB9, propertyPVALID}, // GUJARATI LETTER VA..GUJARATI LETTER HA
+ {0x0ABA, 0x0ABB, propertyUNASSIGNED}, // ..
+ {0x0ABC, 0x0AC5, propertyPVALID}, // GUJARATI SIGN NUKTA..GUJARATI VOWEL SIGN CAN
+ {0x0AC6, 0x0, propertyUNASSIGNED}, //
+ {0x0AC7, 0x0AC9, propertyPVALID}, // GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN C
+ {0x0ACA, 0x0, propertyUNASSIGNED}, //
+ {0x0ACB, 0x0ACD, propertyPVALID}, // GUJARATI VOWEL SIGN O..GUJARATI SIGN VIRAMA
+ {0x0ACE, 0x0ACF, propertyUNASSIGNED}, // ..
+ {0x0AD0, 0x0, propertyPVALID}, // GUJARATI OM
+ {0x0AD1, 0x0ADF, propertyUNASSIGNED}, // ..
+ {0x0AE0, 0x0AE3, propertyPVALID}, // GUJARATI LETTER VOCALIC RR..GUJARATI VOWEL S
+ {0x0AE4, 0x0AE5, propertyUNASSIGNED}, // ..
+ {0x0AE6, 0x0AEF, propertyPVALID}, // GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
+ {0x0AF0, 0x0, propertyUNASSIGNED}, //
+ {0x0AF1, 0x0, propertyDISALLOWED}, // GUJARATI RUPEE SIGN
+ {0x0AF2, 0x0B00, propertyUNASSIGNED}, // ..
+ {0x0B01, 0x0B03, propertyPVALID}, // ORIYA SIGN CANDRABINDU..ORIYA SIGN VISARGA
+ {0x0B04, 0x0, propertyUNASSIGNED}, //
+ {0x0B05, 0x0B0C, propertyPVALID}, // ORIYA LETTER A..ORIYA LETTER VOCALIC L
+ {0x0B0D, 0x0B0E, propertyUNASSIGNED}, // ..
+ {0x0B0F, 0x0B10, propertyPVALID}, // ORIYA LETTER E..ORIYA LETTER AI
+ {0x0B11, 0x0B12, propertyUNASSIGNED}, // ..
+ {0x0B13, 0x0B28, propertyPVALID}, // ORIYA LETTER O..ORIYA LETTER NA
+ {0x0B29, 0x0, propertyUNASSIGNED}, //
+ {0x0B2A, 0x0B30, propertyPVALID}, // ORIYA LETTER PA..ORIYA LETTER RA
+ {0x0B31, 0x0, propertyUNASSIGNED}, //
+ {0x0B32, 0x0B33, propertyPVALID}, // ORIYA LETTER LA..ORIYA LETTER LLA
+ {0x0B34, 0x0, propertyUNASSIGNED}, //
+ {0x0B35, 0x0B39, propertyPVALID}, // ORIYA LETTER VA..ORIYA LETTER HA
+ {0x0B3A, 0x0B3B, propertyUNASSIGNED}, // ..
+ {0x0B3C, 0x0B44, propertyPVALID}, // ORIYA SIGN NUKTA..ORIYA VOWEL SIGN VOCALIC R
+ {0x0B45, 0x0B46, propertyUNASSIGNED}, // ..
+ {0x0B47, 0x0B48, propertyPVALID}, // ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+ {0x0B49, 0x0B4A, propertyUNASSIGNED}, // ..
+ {0x0B4B, 0x0B4D, propertyPVALID}, // ORIYA VOWEL SIGN O..ORIYA SIGN VIRAMA
+ {0x0B4E, 0x0B55, propertyUNASSIGNED}, // ..
+ {0x0B56, 0x0B57, propertyPVALID}, // ORIYA AI LENGTH MARK..ORIYA AU LENGTH MARK
+ {0x0B58, 0x0B5B, propertyUNASSIGNED}, // ..
+ {0x0B5C, 0x0B5D, propertyDISALLOWED}, // ORIYA LETTER RRA..ORIYA LETTER RHA
+ {0x0B5E, 0x0, propertyUNASSIGNED}, //
+ {0x0B5F, 0x0B63, propertyPVALID}, // ORIYA LETTER YYA..ORIYA VOWEL SIGN VOCALIC L
+ {0x0B64, 0x0B65, propertyUNASSIGNED}, // ..
+ {0x0B66, 0x0B6F, propertyPVALID}, // ORIYA DIGIT ZERO..ORIYA DIGIT NINE
+ {0x0B70, 0x0, propertyDISALLOWED}, // ORIYA ISSHAR
+ {0x0B71, 0x0, propertyPVALID}, // ORIYA LETTER WA
+ {0x0B72, 0x0B81, propertyUNASSIGNED}, // ..
+ {0x0B82, 0x0B83, propertyPVALID}, // TAMIL SIGN ANUSVARA..TAMIL SIGN VISARGA
+ {0x0B84, 0x0, propertyUNASSIGNED}, //
+ {0x0B85, 0x0B8A, propertyPVALID}, // TAMIL LETTER A..TAMIL LETTER UU
+ {0x0B8B, 0x0B8D, propertyUNASSIGNED}, // ..
+ {0x0B8E, 0x0B90, propertyPVALID}, // TAMIL LETTER E..TAMIL LETTER AI
+ {0x0B91, 0x0, propertyUNASSIGNED}, //
+ {0x0B92, 0x0B95, propertyPVALID}, // TAMIL LETTER O..TAMIL LETTER KA
+ {0x0B96, 0x0B98, propertyUNASSIGNED}, // ..
+ {0x0B99, 0x0B9A, propertyPVALID}, // TAMIL LETTER NGA..TAMIL LETTER CA
+ {0x0B9B, 0x0, propertyUNASSIGNED}, //
+ {0x0B9C, 0x0, propertyPVALID}, // TAMIL LETTER JA
+ {0x0B9D, 0x0, propertyUNASSIGNED}, //
+ {0x0B9E, 0x0B9F, propertyPVALID}, // TAMIL LETTER NYA..TAMIL LETTER TTA
+ {0x0BA0, 0x0BA2, propertyUNASSIGNED}, // ..
+ {0x0BA3, 0x0BA4, propertyPVALID}, // TAMIL LETTER NNA..TAMIL LETTER TA
+ {0x0BA5, 0x0BA7, propertyUNASSIGNED}, // ..
+ {0x0BA8, 0x0BAA, propertyPVALID}, // TAMIL LETTER NA..TAMIL LETTER PA
+ {0x0BAB, 0x0BAD, propertyUNASSIGNED}, // ..
+ {0x0BAE, 0x0BB9, propertyPVALID}, // TAMIL LETTER MA..TAMIL LETTER HA
+ {0x0BBA, 0x0BBD, propertyUNASSIGNED}, // ..
+ {0x0BBE, 0x0BC2, propertyPVALID}, // TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN UU
+ {0x0BC3, 0x0BC5, propertyUNASSIGNED}, // ..
+ {0x0BC6, 0x0BC8, propertyPVALID}, // TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+ {0x0BC9, 0x0, propertyUNASSIGNED}, //
+ {0x0BCA, 0x0BCD, propertyPVALID}, // TAMIL VOWEL SIGN O..TAMIL SIGN VIRAMA
+ {0x0BCE, 0x0BCF, propertyUNASSIGNED}, // ..
+ {0x0BD0, 0x0, propertyPVALID}, // TAMIL OM
+ {0x0BD1, 0x0BD6, propertyUNASSIGNED}, // ..
+ {0x0BD7, 0x0, propertyPVALID}, // TAMIL AU LENGTH MARK
+ {0x0BD8, 0x0BE5, propertyUNASSIGNED}, // ..
+ {0x0BE6, 0x0BEF, propertyPVALID}, // TAMIL DIGIT ZERO..TAMIL DIGIT NINE
+ {0x0BF0, 0x0BFA, propertyDISALLOWED}, // TAMIL NUMBER TEN..TAMIL NUMBER SIGN
+ {0x0BFB, 0x0C00, propertyUNASSIGNED}, // ..
+ {0x0C01, 0x0C03, propertyPVALID}, // TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+ {0x0C04, 0x0, propertyUNASSIGNED}, //
+ {0x0C05, 0x0C0C, propertyPVALID}, // TELUGU LETTER A..TELUGU LETTER VOCALIC L
+ {0x0C0D, 0x0, propertyUNASSIGNED}, //
+ {0x0C0E, 0x0C10, propertyPVALID}, // TELUGU LETTER E..TELUGU LETTER AI
+ {0x0C11, 0x0, propertyUNASSIGNED}, //
+ {0x0C12, 0x0C28, propertyPVALID}, // TELUGU LETTER O..TELUGU LETTER NA
+ {0x0C29, 0x0, propertyUNASSIGNED}, //
+ {0x0C2A, 0x0C33, propertyPVALID}, // TELUGU LETTER PA..TELUGU LETTER LLA
+ {0x0C34, 0x0, propertyUNASSIGNED}, //
+ {0x0C35, 0x0C39, propertyPVALID}, // TELUGU LETTER VA..TELUGU LETTER HA
+ {0x0C3A, 0x0C3C, propertyUNASSIGNED}, // ..
+ {0x0C3D, 0x0C44, propertyPVALID}, // TELUGU SIGN AVAGRAHA..TELUGU VOWEL SIGN VOCA
+ {0x0C45, 0x0, propertyUNASSIGNED}, //
+ {0x0C46, 0x0C48, propertyPVALID}, // TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+ {0x0C49, 0x0, propertyUNASSIGNED}, //
+ {0x0C4A, 0x0C4D, propertyPVALID}, // TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+ {0x0C4E, 0x0C54, propertyUNASSIGNED}, // ..
+ {0x0C55, 0x0C56, propertyPVALID}, // TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+ {0x0C57, 0x0, propertyUNASSIGNED}, //
+ {0x0C58, 0x0C59, propertyPVALID}, // TELUGU LETTER TSA..TELUGU LETTER DZA
+ {0x0C5A, 0x0C5F, propertyUNASSIGNED}, // ..
+ {0x0C60, 0x0C63, propertyPVALID}, // TELUGU LETTER VOCALIC RR..TELUGU VOWEL SIGN
+ {0x0C64, 0x0C65, propertyUNASSIGNED}, // ..
+ {0x0C66, 0x0C6F, propertyPVALID}, // TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+ {0x0C70, 0x0C77, propertyUNASSIGNED}, // ..
+ {0x0C78, 0x0C7F, propertyDISALLOWED}, // TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF
+ {0x0C80, 0x0C81, propertyUNASSIGNED}, // ..
+ {0x0C82, 0x0C83, propertyPVALID}, // KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+ {0x0C84, 0x0, propertyUNASSIGNED}, //
+ {0x0C85, 0x0C8C, propertyPVALID}, // KANNADA LETTER A..KANNADA LETTER VOCALIC L
+ {0x0C8D, 0x0, propertyUNASSIGNED}, //
+ {0x0C8E, 0x0C90, propertyPVALID}, // KANNADA LETTER E..KANNADA LETTER AI
+ {0x0C91, 0x0, propertyUNASSIGNED}, //
+ {0x0C92, 0x0CA8, propertyPVALID}, // KANNADA LETTER O..KANNADA LETTER NA
+ {0x0CA9, 0x0, propertyUNASSIGNED}, //
+ {0x0CAA, 0x0CB3, propertyPVALID}, // KANNADA LETTER PA..KANNADA LETTER LLA
+ {0x0CB4, 0x0, propertyUNASSIGNED}, //
+ {0x0CB5, 0x0CB9, propertyPVALID}, // KANNADA LETTER VA..KANNADA LETTER HA
+ {0x0CBA, 0x0CBB, propertyUNASSIGNED}, // ..
+ {0x0CBC, 0x0CC4, propertyPVALID}, // KANNADA SIGN NUKTA..KANNADA VOWEL SIGN VOCAL
+ {0x0CC5, 0x0, propertyUNASSIGNED}, //
+ {0x0CC6, 0x0CC8, propertyPVALID}, // KANNADA VOWEL SIGN E..KANNADA VOWEL SIGN AI
+ {0x0CC9, 0x0, propertyUNASSIGNED}, //
+ {0x0CCA, 0x0CCD, propertyPVALID}, // KANNADA VOWEL SIGN O..KANNADA SIGN VIRAMA
+ {0x0CCE, 0x0CD4, propertyUNASSIGNED}, // ..
+ {0x0CD5, 0x0CD6, propertyPVALID}, // KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+ {0x0CD7, 0x0CDD, propertyUNASSIGNED}, // ..
+ {0x0CDE, 0x0, propertyPVALID}, // KANNADA LETTER FA
+ {0x0CDF, 0x0, propertyUNASSIGNED}, //
+ {0x0CE0, 0x0CE3, propertyPVALID}, // KANNADA LETTER VOCALIC RR..KANNADA VOWEL SIG
+ {0x0CE4, 0x0CE5, propertyUNASSIGNED}, // ..
+ {0x0CE6, 0x0CEF, propertyPVALID}, // KANNADA DIGIT ZERO..KANNADA DIGIT NINE
+ {0x0CF0, 0x0, propertyUNASSIGNED}, //
+ {0x0CF1, 0x0CF2, propertyDISALLOWED}, // KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADH
+ {0x0CF3, 0x0D01, propertyUNASSIGNED}, // ..
+ {0x0D02, 0x0D03, propertyPVALID}, // MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISA
+ {0x0D04, 0x0, propertyUNASSIGNED}, //
+ {0x0D05, 0x0D0C, propertyPVALID}, // MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC
+ {0x0D0D, 0x0, propertyUNASSIGNED}, //
+ {0x0D0E, 0x0D10, propertyPVALID}, // MALAYALAM LETTER E..MALAYALAM LETTER AI
+ {0x0D11, 0x0, propertyUNASSIGNED}, //
+ {0x0D12, 0x0D28, propertyPVALID}, // MALAYALAM LETTER O..MALAYALAM LETTER NA
+ {0x0D29, 0x0, propertyUNASSIGNED}, //
+ {0x0D2A, 0x0D39, propertyPVALID}, // MALAYALAM LETTER PA..MALAYALAM LETTER HA
+ {0x0D3A, 0x0D3C, propertyUNASSIGNED}, // ..
+ {0x0D3D, 0x0D44, propertyPVALID}, // MALAYALAM SIGN AVAGRAHA..MALAYALAM VOWEL SIG
+ {0x0D45, 0x0, propertyUNASSIGNED}, //
+ {0x0D46, 0x0D48, propertyPVALID}, // MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN
+ {0x0D49, 0x0, propertyUNASSIGNED}, //
+ {0x0D4A, 0x0D4D, propertyPVALID}, // MALAYALAM VOWEL SIGN O..MALAYALAM SIGN VIRAM
+ {0x0D4E, 0x0D56, propertyUNASSIGNED}, // ..
+ {0x0D57, 0x0, propertyPVALID}, // MALAYALAM AU LENGTH MARK
+ {0x0D58, 0x0D5F, propertyUNASSIGNED}, // ..
+ {0x0D60, 0x0D63, propertyPVALID}, // MALAYALAM LETTER VOCALIC RR..MALAYALAM VOWEL
+ {0x0D64, 0x0D65, propertyUNASSIGNED}, // ..
+ {0x0D66, 0x0D6F, propertyPVALID}, // MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
+ {0x0D70, 0x0D75, propertyDISALLOWED}, // MALAYALAM NUMBER TEN..MALAYALAM FRACTION THR
+ {0x0D76, 0x0D78, propertyUNASSIGNED}, // ..
+ {0x0D79, 0x0, propertyDISALLOWED}, // MALAYALAM DATE MARK
+ {0x0D7A, 0x0D7F, propertyPVALID}, // MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER
+ {0x0D80, 0x0D81, propertyUNASSIGNED}, // ..
+ {0x0D82, 0x0D83, propertyPVALID}, // SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARG
+ {0x0D84, 0x0, propertyUNASSIGNED}, //
+ {0x0D85, 0x0D96, propertyPVALID}, // SINHALA LETTER AYANNA..SINHALA LETTER AUYANN
+ {0x0D97, 0x0D99, propertyUNASSIGNED}, // ..
+ {0x0D9A, 0x0DB1, propertyPVALID}, // SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA L
+ {0x0DB2, 0x0, propertyUNASSIGNED}, //
+ {0x0DB3, 0x0DBB, propertyPVALID}, // SINHALA LETTER SANYAKA DAYANNA..SINHALA LETT
+ {0x0DBC, 0x0, propertyUNASSIGNED}, //
+ {0x0DBD, 0x0, propertyPVALID}, // SINHALA LETTER DANTAJA LAYANNA
+ {0x0DBE, 0x0DBF, propertyUNASSIGNED}, // ..
+ {0x0DC0, 0x0DC6, propertyPVALID}, // SINHALA LETTER VAYANNA..SINHALA LETTER FAYAN
+ {0x0DC7, 0x0DC9, propertyUNASSIGNED}, // ..
+ {0x0DCA, 0x0, propertyPVALID}, // SINHALA SIGN AL-LAKUNA
+ {0x0DCB, 0x0DCE, propertyUNASSIGNED}, // ..
+ {0x0DCF, 0x0DD4, propertyPVALID}, // SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL
+ {0x0DD5, 0x0, propertyUNASSIGNED}, //
+ {0x0DD6, 0x0, propertyPVALID}, // SINHALA VOWEL SIGN DIGA PAA-PILLA
+ {0x0DD7, 0x0, propertyUNASSIGNED}, //
+ {0x0DD8, 0x0DDF, propertyPVALID}, // SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOW
+ {0x0DE0, 0x0DF1, propertyUNASSIGNED}, // ..
+ {0x0DF2, 0x0DF3, propertyPVALID}, // SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHAL
+ {0x0DF4, 0x0, propertyDISALLOWED}, // SINHALA PUNCTUATION KUNDDALIYA
+ {0x0DF5, 0x0E00, propertyUNASSIGNED}, // ..
+ {0x0E01, 0x0E32, propertyPVALID}, // THAI CHARACTER KO KAI..THAI CHARACTER SARA A
+ {0x0E33, 0x0, propertyDISALLOWED}, // THAI CHARACTER SARA AM
+ {0x0E34, 0x0E3A, propertyPVALID}, // THAI CHARACTER SARA I..THAI CHARACTER PHINTH
+ {0x0E3B, 0x0E3E, propertyUNASSIGNED}, // ..
+ {0x0E3F, 0x0, propertyDISALLOWED}, // THAI CURRENCY SYMBOL BAHT
+ {0x0E40, 0x0E4E, propertyPVALID}, // THAI CHARACTER SARA E..THAI CHARACTER YAMAKK
+ {0x0E4F, 0x0, propertyDISALLOWED}, // THAI CHARACTER FONGMAN
+ {0x0E50, 0x0E59, propertyPVALID}, // THAI DIGIT ZERO..THAI DIGIT NINE
+ {0x0E5A, 0x0E5B, propertyDISALLOWED}, // THAI CHARACTER ANGKHANKHU..THAI CHARACTER KH
+ {0x0E5C, 0x0E80, propertyUNASSIGNED}, // ..
+ {0x0E81, 0x0E82, propertyPVALID}, // LAO LETTER KO..LAO LETTER KHO SUNG
+ {0x0E83, 0x0, propertyUNASSIGNED}, //
+ {0x0E84, 0x0, propertyPVALID}, // LAO LETTER KHO TAM
+ {0x0E85, 0x0E86, propertyUNASSIGNED}, // ..
+ {0x0E87, 0x0E88, propertyPVALID}, // LAO LETTER NGO..LAO LETTER CO
+ {0x0E89, 0x0, propertyUNASSIGNED}, //