pax_global_header00006660000000000000000000000064122143011020014474gustar00rootroot0000000000000052 comment=1f586fdcdace10a157f3a9160f69def6c4ff08c3 golang-dns-0.0~git20130912/000077500000000000000000000000001221430110200151105ustar00rootroot00000000000000golang-dns-0.0~git20130912/.gitignore000066400000000000000000000000231221430110200170730ustar00rootroot00000000000000*.6 test.out a.out golang-dns-0.0~git20130912/LICENSE000066400000000000000000000031171221430110200161170ustar00rootroot00000000000000Extensions 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. golang-dns-0.0~git20130912/README.markdown000066400000000000000000000061611221430110200176150ustar00rootroot00000000000000# 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. If you like this, you may also be interested in: * https://github.com/miekg/fks -- a (in)complete nameserver written in Go; * https://github.com/miekg/unbound -- Go wrapper for the Unbound resolver. # 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://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 * more? (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 with ~ 100K RR/s, that's 5M records in about 50 seconds; * Server side programming (mimicking the net/http package); * Client side programming; * DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA; * EDNS0, NSID; * AXFR/IXFR; * TSIG; * DNS name compression. Have fun! Miek Gieben - 2010-2012 - miek@miek.nl # Building Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should work: go get github.com/miekg/dns go build github/com/miekg/dns A short "how to use the API" is at the beginning of dns.go (this also will show when you call `go doc github.com/miekg/dns`. Sample programs can be found in the `ex` directory. They can also be build with: `go build`. ## Supported RFCs *all of them* * 103{4,5} - DNS standard * 1982 - Serial Arithmetic * 1876 - LOC record * 1995 - IXFR * 1996 - DNS notify * 2136 - DNS Update (dynamic updates) * 2181 - RRset definition * 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 - Unkown 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 * 6605 - ECDSA * 6742 - ILNP DNS * 6891 - EDNS0 update * xxxx - URI record (draft) * xxxx - EDNS0 DNS Update Lease (draft) * xxxx - IEU48/IEU64 records (draft) * xxxx - Algorithm-Signal (draft) ## Loosely based upon * `ldns` * `NSD` * `Net::DNS` * `GRONG` golang-dns-0.0~git20130912/TODO.markdown000066400000000000000000000007331221430110200174240ustar00rootroot00000000000000# TODO * Support for on-the-fly-signing or check how to do it * Ratelimiting? server side (rrl) * Ratelimiting? client side ## Nice to have * Speed, we can always go faster. A simple reflect server now hits 75/80K qps on moderate hardware * go test; only works correct on my machine * privatekey.Precompute() when signing? ## RRs not implemented These are deprecated, or rarely used (or just a bitch to implement). NSAP NSAP-PTR PX GPOS NIMLOC ATMA A6 KEY SIG NXT golang-dns-0.0~git20130912/client.go000066400000000000000000000151461221430110200167240ustar00rootroot00000000000000// 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. package dns // A client implementation. import ( "io" "net" "time" ) // Order of events: // *client -> *reply -> Exchange() -> dial()/send()->write()/receive()->read() // Do I want make this an interface thingy? type reply struct { client *Client addr string req *Msg conn net.Conn tsigRequestMAC string tsigTimersOnly bool tsigStatus error rtt time.Duration t time.Time } // A Client defines parameter for a DNS client. A nil // Client is usable for sending queries. type Client struct { Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections (ns), defaults to 2 * 1e9 WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections (ns), defaults to 2 * 1e9 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 } func (c *Client) exchangeMerge(m *Msg, a string, s net.Conn) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { if s == nil { return c.exchange(m, a) } return c.exchangeConn(m, s) } // 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) { if s == nil { return c.exchange(m, a) } return c.exchangeConn(m, s) }) if err != nil { return r, rtt, err } if shared { r1 := r.copy() r1.Id = r.Id // Copy Id! r = r1 } return r, rtt, nil } // Exchange performs an synchronous query. It sends the message m to the address // contained in a and waits for an reply. Basic use pattern with a *dns.Client: // // c := new(dns.Client) // in, rtt, err := c.Exchange(message, "127.0.0.1:53") // func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { return c.exchangeMerge(m, a, nil) } func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { w := &reply{client: c, addr: a} if err = w.dial(); err != nil { return nil, 0, err } defer w.conn.Close() if err = w.send(m); err != nil { return nil, 0, err } r, err = w.receive() return r, w.rtt, err } // ExchangeConn performs an synchronous query. It sends the message m trough the // connection s and waits for a reply. func (c *Client) ExchangeConn(m *Msg, s net.Conn) (r *Msg, rtt time.Duration, err error) { return c.exchangeMerge(m, "", s) } func (c *Client) exchangeConn(m *Msg, s net.Conn) (r *Msg, rtt time.Duration, err error) { w := &reply{client: c, conn: s} if err = w.send(m); err != nil { return nil, 0, err } r, err = w.receive() return r, w.rtt, err } // dial connects to the address addr for the network set in c.Net func (w *reply) dial() (err error) { var conn net.Conn if w.client.Net == "" { conn, err = net.DialTimeout("udp", w.addr, 5*1e9) } else { conn, err = net.DialTimeout(w.client.Net, w.addr, 5*1e9) } if err != nil { return err } w.conn = conn return } func (w *reply) receive() (*Msg, error) { var p []byte m := new(Msg) switch w.client.Net { case "tcp", "tcp4", "tcp6": p = make([]byte, MaxMsgSize) case "", "udp", "udp4", "udp6": // OPT! TODO(mg) p = make([]byte, DefaultMsgSize) } n, err := w.read(p) if err != nil && n == 0 { return nil, err } p = p[:n] if err := m.Unpack(p); err != nil { return nil, err } w.rtt = time.Since(w.t) if t := m.IsTsig(); t != nil { secret := t.Hdr.Name if _, ok := w.client.TsigSecret[secret]; !ok { w.tsigStatus = ErrSecret return m, ErrSecret } // Need to work on the original message p, as that was used to calculate the tsig. w.tsigStatus = TsigVerify(p, w.client.TsigSecret[secret], w.tsigRequestMAC, w.tsigTimersOnly) } return m, w.tsigStatus } func (w *reply) read(p []byte) (n int, err error) { if w.conn == nil { return 0, ErrConnEmpty } if len(p) < 2 { return 0, io.ErrShortBuffer } switch w.client.Net { case "tcp", "tcp4", "tcp6": setTimeouts(w) n, err = w.conn.(*net.TCPConn).Read(p[0:2]) if err != nil || n != 2 { return n, err } l, _ := unpackUint16(p[0:2], 0) if l == 0 { return 0, ErrShortRead } if int(l) > len(p) { return int(l), io.ErrShortBuffer } n, err = w.conn.(*net.TCPConn).Read(p[:l]) if err != nil { return n, err } i := n for i < int(l) { j, err := w.conn.(*net.TCPConn).Read(p[i:int(l)]) if err != nil { return i, err } i += j } n = i case "", "udp", "udp4", "udp6": setTimeouts(w) n, _, err = w.conn.(*net.UDPConn).ReadFromUDP(p) if err != nil { return n, err } } return n, err } // send sends a dns msg to the address specified in w. // If the message m contains a TSIG record the transaction // signature is calculated. func (w *reply) send(m *Msg) (err error) { var out []byte if t := m.IsTsig(); t != nil { mac := "" name := t.Hdr.Name if _, ok := w.client.TsigSecret[name]; !ok { return ErrSecret } out, mac, err = TsigGenerate(m, w.client.TsigSecret[name], w.tsigRequestMAC, w.tsigTimersOnly) w.tsigRequestMAC = mac } else { out, err = m.Pack() } if err != nil { return err } w.t = time.Now() if _, err = w.write(out); err != nil { return err } return nil } func (w *reply) write(p []byte) (n int, err error) { switch w.client.Net { case "tcp", "tcp4", "tcp6": if len(p) < 2 { return 0, io.ErrShortBuffer } setTimeouts(w) l := make([]byte, 2) l[0], l[1] = packUint16(uint16(len(p))) p = append(l, p...) n, err := w.conn.Write(p) if err != nil { return n, err } i := n if i < len(p) { j, err := w.conn.Write(p[i:len(p)]) if err != nil { return i, err } i += j } n = i case "", "udp", "udp4", "udp6": setTimeouts(w) n, err = w.conn.(*net.UDPConn).Write(p) if err != nil { return n, err } } return } func setTimeouts(w *reply) { if w.client.ReadTimeout == 0 { w.conn.SetReadDeadline(time.Now().Add(2 * 1e9)) } else { w.conn.SetReadDeadline(time.Now().Add(w.client.ReadTimeout)) } if w.client.WriteTimeout == 0 { w.conn.SetWriteDeadline(time.Now().Add(2 * 1e9)) } else { w.conn.SetWriteDeadline(time.Now().Add(w.client.WriteTimeout)) } } golang-dns-0.0~git20130912/client_test.go000066400000000000000000000062761221430110200177670ustar00rootroot00000000000000// 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. package dns import ( "testing" "time" ) func TestClientSync(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeSOA) c := new(Client) r, _, _ := c.Exchange(m, "37.251.95.53:53") if r != nil && r.Rcode != RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() t.Logf("%v\n", r) } } func TestClientEDNS0(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeDNSKEY) m.SetEdns0(2048, true) //edns.Option = make([]Option, 1) //edns.SetNsid("") // Empty to request it c := new(Client) r, _, _ := c.Exchange(m, "37.251.95.53:53") if r != nil && r.Rcode != RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() t.Logf("%v\n", r) } } func TestSingleSingleInflight(t *testing.T) { m := new(Msg) m.SetQuestion("miek.nl.", TypeDNSKEY) c := new(Client) c.SingleInflight = true nr := 10 ch := make(chan time.Duration) for i := 0; i < nr; i++ { go func() { _, rtt, _ := c.Exchange(m, "37.251.95.53:53") ch <- rtt }() } i := 0 var first time.Duration // With inflight *all* rtt are identical, and by doing actual lookups // the changes that this is a coincidence is small. Loop: for { select { case rtt := <-ch: if i == 0 { first = rtt } else { if first != rtt { t.Log("All rtt should be equal") t.Fail() } } i++ if i == 10 { break Loop } } } } func TestClientTsigAXFR(t *testing.T) { m := new(Msg) m.SetAxfr("miek.nl.") m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix()) c := new(Client) c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} c.Net = "tcp" if a, err := c.TransferIn(m, "37.251.95.53:53"); err != nil { t.Log("Failed to setup axfr: " + err.Error()) t.Fatal() } else { for ex := range a { if ex.Error != nil { t.Logf("Error %s\n", ex.Error.Error()) t.Fail() break } for _, rr := range ex.RR { t.Logf("%s\n", rr.String()) } } } } func TestClientAXFRMultipleMessages(t *testing.T) { m := new(Msg) m.SetAxfr("dnsex.nl.") c := new(Client) c.Net = "tcp" if a, err := c.TransferIn(m, "37.251.95.53:53"); err != nil { t.Log("Failed to setup axfr" + err.Error()) t.Fail() return } else { for ex := range a { if ex.Error != nil { t.Logf("Error %s\n", ex.Error.Error()) t.Fail() break } } } } // not really a test, but shows how to use update leases func TestUpdateLeaseTSIG(t *testing.T) { 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) lease_rr := new(OPT) lease_rr.Hdr.Name = "." lease_rr.Hdr.Rrtype = TypeOPT e := new(EDNS0_UL) e.Code = EDNS0UL e.Lease = 120 lease_rr.Option = append(lease_rr.Option, e) m.Extra = append(m.Extra, lease_rr) c := new(Client) m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix()) c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="} w := new(reply) w.client = c w.addr = "127.0.0.1:53" w.req = m if err := w.dial(); err != nil { t.Fail() } if err := w.send(m); err != nil { t.Fail() } } golang-dns-0.0~git20130912/clientconfig.go000066400000000000000000000052331221430110200201060ustar00rootroot00000000000000// 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 package dns import ( "bufio" "os" "strconv" "strings" ) // Wraps the contents of the /etc/resolv.conf. 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(conf string) (*ClientConfig, error) { file, err := os.Open(conf) if err != nil { return nil, err } defer file.Close() c := new(ClientConfig) b := bufio.NewReader(file) c.Servers = make([]string, 0) c.Search = make([]string, 0) c.Port = "53" c.Ndots = 1 c.Timeout = 5 c.Attempts = 2 for line, ok := b.ReadString('\n'); ok == nil; line, ok = b.ReadString('\n') { 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] // Don't use this. net.JoinHostPort will fix this for you // switch x := net.ParseIP(name); true { // case x.To4() != nil: // c.Servers = append(c.Servers, name) // case x.To16() != nil: // name = "[" + name + "]" 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 } golang-dns-0.0~git20130912/contrib/000077500000000000000000000000001221430110200165505ustar00rootroot00000000000000golang-dns-0.0~git20130912/contrib/check-soa/000077500000000000000000000000001221430110200204055ustar00rootroot00000000000000golang-dns-0.0~git20130912/contrib/check-soa/check-soa.go000066400000000000000000000101741221430110200225740ustar00rootroot00000000000000// Go equivalent of the "DNS & BIND" book check-soa program. // Created by Stephane Bortzmeyer. package main import ( "errors" "fmt" "github.com/miekg/dns" "os" "strings" "time" ) const ( TIMEOUT time.Duration = 5 // seconds ) var ( localm *dns.Msg localc *dns.Client conf *dns.ClientConfig ) func localQuery(qname string, qtype uint16) (*dns.Msg, error) { localm.SetQuestion(qname, qtype) for i := range conf.Servers { server := conf.Servers[i] r, _, err := localc.Exchange(localm, server+":"+conf.Port) if r == nil || r.Rcode == dns.RcodeNameError || r.Rcode == dns.RcodeSuccess { return r, err } } return nil, errors.New("No name server to answer the question") } func main() { var err error if len(os.Args) != 2 { fmt.Printf("%s ZONE\n", os.Args[0]) os.Exit(1) } conf, err = dns.ClientConfigFromFile("/etc/resolv.conf") if conf == nil { fmt.Printf("Cannot initialize the local resolver: %s\n", err) os.Exit(1) } localm = new(dns.Msg) localm.RecursionDesired = true localm.Question = make([]dns.Question, 1) localc = new(dns.Client) localc.ReadTimeout = TIMEOUT * 1e9 r, err := localQuery(dns.Fqdn(os.Args[1]), dns.TypeNS) if r == nil { fmt.Printf("Cannot retrieve the list of name servers for %s: %s\n", dns.Fqdn(os.Args[1]), err) os.Exit(1) } if r.Rcode == dns.RcodeNameError { fmt.Printf("No such domain %s\n", dns.Fqdn(os.Args[1])) os.Exit(1) } m := new(dns.Msg) m.RecursionDesired = false m.Question = make([]dns.Question, 1) c := new(dns.Client) c.ReadTimeout = TIMEOUT * 1e9 success := true numNS := 0 for _, ans := range r.Answer { switch ans.(type) { case *dns.NS: nameserver := ans.(*dns.NS).Ns numNS += 1 ips := make([]string, 0) fmt.Printf("%s : ", nameserver) ra, err := localQuery(nameserver, dns.TypeA) if ra == nil { fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, err) os.Exit(1) } if ra.Rcode != dns.RcodeSuccess { fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, dns.RcodeToString[ra.Rcode]) os.Exit(1) } for _, ansa := range ra.Answer { switch ansa.(type) { case *dns.A: ips = append(ips, ansa.(*dns.A).A.String()) } } raaaa, err := localQuery(nameserver, dns.TypeAAAA) if raaaa == nil { fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, err) os.Exit(1) } if raaaa.Rcode != dns.RcodeSuccess { fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, dns.RcodeToString[raaaa.Rcode]) os.Exit(1) } for _, ansaaaa := range raaaa.Answer { switch ansaaaa.(type) { case *dns.AAAA: ips = append(ips, ansaaaa.(*dns.AAAA).AAAA.String()) } } if len(ips) == 0 { success = false fmt.Printf("No IP address for this server") } for _, ip := range ips { m.Question[0] = dns.Question{dns.Fqdn(os.Args[1]), dns.TypeSOA, dns.ClassINET} nsAddressPort := "" if strings.ContainsAny(":", ip) { // IPv6 address nsAddressPort = "[" + ip + "]:53" } else { nsAddressPort = ip + ":53" } soa, _, err := c.Exchange(m, nsAddressPort) // TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end if soa == nil { success = false fmt.Printf("%s (%s) ", ip, err) goto Next } if soa.Rcode != dns.RcodeSuccess { success = false fmt.Printf("%s (%s) ", ips, dns.RcodeToString[soa.Rcode]) goto Next } if len(soa.Answer) == 0 { // May happen if the server is a recursor, not authoritative, since we query with RD=0 success = false fmt.Printf("%s (0 answer) ", ip) goto Next } rsoa := soa.Answer[0] switch rsoa.(type) { case *dns.SOA: if soa.Authoritative { // TODO: test if all name servers have the same serial ? fmt.Printf("%s (%d) ", ips, rsoa.(*dns.SOA).Serial) } else { success = false fmt.Printf("%s (not authoritative) ", ips) } } } Next: fmt.Printf("\n") } } if numNS == 0 { fmt.Printf("No NS records for \"%s\". It is probably a CNAME to a domain but not a zone\n", dns.Fqdn(os.Args[1])) os.Exit(1) } if success { os.Exit(0) } os.Exit(1) } golang-dns-0.0~git20130912/defaults.go000066400000000000000000000151441221430110200172530ustar00rootroot00000000000000// 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. package dns import ( "net" "strconv" ) const hexDigit = "0123456789abcdef" // Everything is assumed in the ClassINET class. If // you need other classes you are on your own. // SetReply creates a reply packet 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 packet. 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 packet. 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 packet suitable for the request. func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg { dns.Rcode = rcode dns.Opcode = OpcodeQuery dns.Response = true dns.Id = request.Id // Note that this is actually a FORMERR if len(request.Question) > 0 { dns.Question = make([]Question, 1) dns.Question[0] = request.Question[0] } return dns } // SetRcodeFormatError creates a packet 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 packet. 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 dns.Msg for requesting an IXFR. func (dns *Msg) SetIxfr(z string, serial uint32) *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 dns.Question[0] = Question{z, TypeIXFR, ClassINET} dns.Ns[0] = s return dns } // SetAxfr creates dns.Msg for requesting an AXFR. func (dns *Msg) SetAxfr(z string) *Msg { dns.Id = Id() dns.Question = make([]Question, 1) dns.Question[0] = Question{z, TypeAXFR, ClassINET} return dns } // SetTsig appends a TSIG RR to the message. // This is only a skeleton TSIG RR that is added as the last RR in the // additional section. The Tsig is calculated when the message is being send. func (dns *Msg) SetTsig(z, algo string, fudge, timesigned int64) *Msg { t := new(TSIG) t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} t.Algorithm = algo t.Fudge = 300 t.TimeSigned = uint64(timesigned) t.OrigId = dns.Id dns.Extra = append(dns.Extra, t) return dns } // SetEdns0 appends a EDNS0 OPT RR to the message. // TSIG should always the last RR in a message. func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg { e := new(OPT) e.Hdr.Name = "." e.Hdr.Rrtype = TypeOPT e.SetUDPSize(udpsize) if do { e.SetDo() } dns.Extra = append(dns.Extra, e) return dns } // IsTsig checks if the message has a TSIG record as the last record // in the additional section. It returns the TSIG record found or nil. func (dns *Msg) IsTsig() *TSIG { if len(dns.Extra) > 0 { if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG { return dns.Extra[len(dns.Extra)-1].(*TSIG) } } return nil } // IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0 // record in the additional section will do. It returns the OPT record // found or nil. func (dns *Msg) IsEdns0() *OPT { for _, r := range dns.Extra { if r.Header().Rrtype == TypeOPT { return r.(*OPT) } } return nil } // IsDomainName checks if s is a valid domainname, it returns // the number of labels and true, when a domain name is valid. // Note that non fully qualified domain name is considered valid, in this case the // last label is counted in the number of labels. // When false is returned the number of labels is not defined. func IsDomainName(s string) (labels int, ok bool) { _, labels, err := packDomainName(s, nil, 0, nil, false) return labels, err == nil } // IsSubDomain checks if child is indeed a child of the parent. Both child and // parent are *not* downcased before doing the comparison. func IsSubDomain(parent, child string) bool { // Entire child is contained in parent return CompareDomainName(parent, child) == CountLabel(parent) } // 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] == '.' } // Fqdns 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 addr suitable for rDNS (PTR) record lookup 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 } else { return "TYPE" + strconv.Itoa(int(t)) } panic("dns: not reached") // go < 1.1 compat } // String returns the string representation for the class c func (c Class) String() string { if c1, ok := ClassToString[uint16(c)]; ok { return c1 } else { return "CLASS" + strconv.Itoa(int(c)) } panic("dns: not reached") } golang-dns-0.0~git20130912/dns.go000066400000000000000000000133061221430110200162260ustar00rootroot00000000000000// 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 // 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 AXFR/IXFR, // 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.Pref = 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 := dns.new(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 = Id() // m1.RecursionDesired = true // m1.Question = make([]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(Client) // in, rtt, err := c.Exchange(m1, "127.0.0.1:53") // // For asynchronous queries it is easy to wrap Exchange() in a goroutine. Suppressing // multiple outstanding queries (with the same question, type and class) is as easy as setting: // // c.SingleInflight = true // // 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 // } package dns import ( "strconv" ) const ( year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. DefaultMsgSize = 4096 // Standard default for larger than 512 packets. udpMsgSize = 512 // Default buffer size for servers receiving UDP packets. MaxMsgSize = 65536 // Largest possible DNS packet. defaultTtl = 3600 // Default TTL. ) // 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 octects) of the uncompressed RR in wire format. len() int } // DNS resource records. // There are many types of RRs, // but they all share the same header. type RR_Header struct { Name string `dns:"cdomain-name"` Rrtype uint16 Class uint16 Ttl uint32 Rdlength uint16 // length of data after header } func (h *RR_Header) Header() *RR_Header { return h } // Just to imlement the RR interface func (h *RR_Header) copy() RR { return nil } func (h *RR_Header) copyHeader() *RR_Header { r := new(RR_Header) r.Name = h.Name r.Rrtype = h.Rrtype r.Class = h.Class r.Ttl = h.Ttl r.Rdlength = h.Rdlength return r } func (h *RR_Header) String() string { var s string if h.Rrtype == TypeOPT { s = ";" // and maybe other things } if len(h.Name) == 0 { s += ".\t" } else { s += 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 } // find best matching pattern for zone func zoneMatch(pattern, zone string) (ok bool) { if len(pattern) == 0 { return } if len(zone) == 0 { zone = "." } // pattern = Fqdn(pattern) // should already be a fqdn zone = Fqdn(zone) i := 0 for { ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i] i++ if !ok { break } if len(pattern)-1-i < 0 || len(zone)-1-i < 0 { break } } return } // ToRFC3597 converts a known RR to the unknown RR representation // from RFC 3597. func (rr *RFC3597) ToRFC3597(r RR) error { buf := make([]byte, r.len()*2) off, err := PackStruct(r, buf, 0) if err != nil { return err } buf = buf[:off] rawSetRdlength(buf, 0, off) _, err = UnpackStruct(rr, buf, 0) if err != nil { return err } return nil } golang-dns-0.0~git20130912/dns_test.go000066400000000000000000000174431221430110200172730ustar00rootroot00000000000000// 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. package dns import ( "net" "testing" ) // Query with way to long name //./q mx bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.bla.miek.nl.miek.nl.miek123.nl. 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.Log("Failed to pack msg with DNSKEY") t.Fail() } in := new(Msg) if in.Unpack(msg) != nil { t.Log("Failed to unpack msg with DNSKEY") t.Fail() } 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.Log("Failed to pack msg with RRSIG") t.Fail() } if in.Unpack(msg) != nil { t.Log("Failed to unpack msg with RRSIG") t.Fail() } } 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.Log("Packing failed") t.Fail() return } } func TestBailiwick(t *testing.T) { yes := map[string]string{ "miek.nl": "ns.miek.nl", ".": "miek.nl", } for parent, child := range yes { if !IsSubDomain(parent, child) { t.Logf("%s should be child of %s\n", child, parent) t.Logf("comparelabels %d", CompareDomainName(parent, child)) t.Logf("lenlabels %d %d", CountLabel(parent), CountLabel(child)) t.Fail() } } 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.Logf("%s should not be child of %s\n", child, parent) t.Logf("comparelabels %d", CompareDomainName(parent, child)) t.Logf("lenlabels %d %d", CountLabel(parent), CountLabel(child)) t.Fail() } } } 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.Logf("Failed to create RR: %s\n", err.Error()) t.Fail() continue } if _, err := m.Pack(); err != nil { t.Logf("Packing failed: %s\n", err.Error()) t.Fail() } } 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.Log("Packing should fail") t.Fail() } x.Answer = make([]RR, 1) x.Answer[0], err = NewRR(rr[0]) if _, err := x.Pack(); err == nil { t.Log("Packing should fail") t.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.Log("Packing should fail") t.Fail() } } 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 TestMsgLenTest(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) t.Fail() } if predicted != len(buf) { t.Errorf("Predicted length is wrong: predicted %s (len=%d) %d, actual %d\n", msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) t.Fail() } } } func BenchmarkMsgLen(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 BenchmarkMsgLenPack(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++ { b, _ := msg.Pack() _ = len(b) } } 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 IN A \# 4 0a000101` { t.Fail() } } func TestNoRdataPack(t *testing.T) { data := make([]byte, 1024) for typ, fn := range rr_mk { r := fn() *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600} _, e := PackRR(r, data, 0, nil, false) if e != nil { t.Logf("Failed to pack RR with zero rdata: %s: %s\n", TypeToString[typ], e.Error()) t.Fail() } } } // TODO(miek): fix dns buffer too small errors this throws func TestNoRdataUnpack(t *testing.T) { data := make([]byte, 1024) for typ, fn := range rr_mk { if typ == TypeSOA || typ == TypeTSIG || typ == TypeWKS { // SOA, TSIG will not be seen (like this) in dyn. updates? // WKS is an bug, but...deprecated record. continue } r := fn() *r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600} off, e := PackRR(r, data, 0, nil, false) if e != nil { // Should always works, TestNoDataPack should have catched this continue } rr, _, e := UnpackRR(data[:off], 0) if e != nil { t.Logf("Failed to unpack RR with zero rdata: %s: %s\n", TypeToString[typ], e.Error()) t.Fail() } t.Logf("%s\n", rr) } } golang-dns-0.0~git20130912/dnssec.go000066400000000000000000000444441221430110200167300ustar00rootroot00000000000000// 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. // DNSSEC // // DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It // uses public key cryptography to sign resource records. The // public keys are stored in DNSKEY records and the signatures in RRSIG records. // // Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit // to an request. // // m := new(dns.Msg) // m.SetEdns0(4096, true) // // Signature generation, signature verification and key generation are all supported. package dns import ( "bytes" "crypto" "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/md5" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/hex" "hash" "io" "math/big" "sort" "strings" "time" ) // DNSSEC encryption algorithm codes. const ( RSAMD5 = 1 DH = 2 DSA = 3 ECC = 4 RSASHA1 = 5 DSANSEC3SHA1 = 6 RSASHA1NSEC3SHA1 = 7 RSASHA256 = 8 RSASHA512 = 10 ECCGOST = 12 ECDSAP256SHA256 = 13 ECDSAP384SHA384 = 14 INDIRECT = 252 PRIVATEDNS = 253 // Private (experimental keys) PRIVATEOID = 254 ) // DNSSEC hashing algorithm codes. const ( _ = iota SHA1 // RFC 4034 SHA256 // RFC 4509 GOST94 // RFC 5933 SHA384 // Experimental SHA512 // Experimental ) // DNSKEY flag values. const ( SEP = 1 ZONE = 1 << 7 REVOKE = 1 << 8 ) // The RRSIG needs to be converted to wireformat with some of // the rdata (the signature) missing. Use this struct to easy // the conversion (and re-use the pack/unpack functions). type rrsigWireFmt struct { TypeCovered uint16 Algorithm uint8 Labels uint8 OrigTtl uint32 Expiration uint32 Inception uint32 KeyTag uint16 SignerName string `dns:"domain-name"` /* No Signature */ } // Used for converting DNSKEY's rdata to wirefmt. type dnskeyWireFmt struct { Flags uint16 Protocol uint8 Algorithm uint8 PublicKey string `dns:"base64"` /* Nothing is left out */ } // 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, _ := packBase64([]byte(k.PublicKey)) if len(modulus) > 1 { x, _ := unpackUint16(modulus, len(modulus)-2) keytag = int(x) } default: keywire := new(dnskeyWireFmt) keywire.Flags = k.Flags keywire.Protocol = k.Protocol keywire.Algorithm = k.Algorithm keywire.PublicKey = k.PublicKey wire := make([]byte, DefaultMsgSize) n, err := PackStruct(keywire, wire, 0) if err != nil { return 0 } wire = wire[:n] for i, v := range wire { if i&1 != 0 { keytag += int(v) // must be larger than uint32 } else { keytag += int(v) << 8 } } keytag += (keytag >> 16) & 0xFFFF keytag &= 0xFFFF } return uint16(keytag) } // ToDS converts a DNSKEY record to a DS record. func (k *DNSKEY) ToDS(h int) *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 = uint8(h) ds.KeyTag = k.KeyTag() keywire := new(dnskeyWireFmt) keywire.Flags = k.Flags keywire.Protocol = k.Protocol keywire.Algorithm = k.Algorithm keywire.PublicKey = k.PublicKey wire := make([]byte, DefaultMsgSize) n, err := PackStruct(keywire, wire, 0) if err != nil { return nil } wire = wire[:n] owner := make([]byte, 255) off, err1 := PackDomainName(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 switch h { case SHA1: s := sha1.New() io.WriteString(s, string(digest)) ds.Digest = hex.EncodeToString(s.Sum(nil)) case SHA256: s := sha256.New() io.WriteString(s, string(digest)) ds.Digest = hex.EncodeToString(s.Sum(nil)) case SHA384: s := sha512.New384() io.WriteString(s, string(digest)) ds.Digest = hex.EncodeToString(s.Sum(nil)) case GOST94: /* I have no clue */ default: return nil } return ds } // Sign signs an RRSet. The signature needs to be filled in with // the values: Inception, Expiration, KeyTag, SignerName and Algorithm. // The rest is copied from the RRset. Sign returns true when the signing went OK, // otherwise false. // There is no check if RRSet is a proper (RFC 2181) RRSet. func (rr *RRSIG) Sign(k PrivateKey, 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 rr.OrigTtl = rrset[0].Header().Ttl rr.TypeCovered = rrset[0].Header().Rrtype rr.TypeCovered = rrset[0].Header().Rrtype rr.Labels = uint8(CountLabel(rrset[0].Header().Name)) if strings.HasPrefix(rrset[0].Header().Name, "*") { rr.Labels-- // wildcard, remove from label count } sigwire := new(rrsigWireFmt) sigwire.TypeCovered = rr.TypeCovered sigwire.Algorithm = rr.Algorithm sigwire.Labels = rr.Labels sigwire.OrigTtl = rr.OrigTtl sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag // For signing, lowercase this name sigwire.SignerName = strings.ToLower(rr.SignerName) // Create the desired binary blob signdata := make([]byte, DefaultMsgSize) n, err := PackStruct(sigwire, signdata, 0) if err != nil { return err } signdata = signdata[:n] wire := rawSignatureData(rrset, rr) if wire == nil { return ErrSigGen } signdata = append(signdata, wire...) var sighash []byte var h hash.Hash var ch crypto.Hash // Only need for RSA switch rr.Algorithm { case DSA, DSANSEC3SHA1: // Implicit in the ParameterSizes case RSASHA1, RSASHA1NSEC3SHA1: h = sha1.New() ch = crypto.SHA1 case RSASHA256, ECDSAP256SHA256: h = sha256.New() ch = crypto.SHA256 case ECDSAP384SHA384: h = sha512.New384() case RSASHA512: h = sha512.New() ch = crypto.SHA512 case RSAMD5: fallthrough // Deprecated in RFC 6725 default: return ErrAlg } io.WriteString(h, string(signdata)) sighash = h.Sum(nil) switch p := k.(type) { case *dsa.PrivateKey: r1, s1, err := dsa.Sign(rand.Reader, p, sighash) if err != nil { return err } signature := []byte{0x4D} // T value, here the ASCII M for Miek (not used in DNSSEC) signature = append(signature, r1.Bytes()...) signature = append(signature, s1.Bytes()...) rr.Signature = unpackBase64(signature) case *rsa.PrivateKey: // We can use nil as rand.Reader here (says AGL) signature, err := rsa.SignPKCS1v15(nil, p, ch, sighash) if err != nil { return err } rr.Signature = unpackBase64(signature) case *ecdsa.PrivateKey: r1, s1, err := ecdsa.Sign(rand.Reader, p, sighash) if err != nil { return err } signature := r1.Bytes() signature = append(signature, s1.Bytes()...) rr.Signature = unpackBase64(signature) default: // Not given the correct key return ErrKeyAlg } return nil } // 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 len(rrset) == 0 { 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 } for _, r := range rrset { if r.Header().Class != rr.Hdr.Class { return ErrRRset } if r.Header().Rrtype != rr.TypeCovered { return ErrRRset } } // RFC 4035 5.3.2. Reconstructing the Signed Data // Copy the sig, except the rrsig data sigwire := new(rrsigWireFmt) sigwire.TypeCovered = rr.TypeCovered sigwire.Algorithm = rr.Algorithm sigwire.Labels = rr.Labels sigwire.OrigTtl = rr.OrigTtl sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag sigwire.SignerName = strings.ToLower(rr.SignerName) // Create the desired binary blob signeddata := make([]byte, DefaultMsgSize) n, err := PackStruct(sigwire, signeddata, 0) if err != nil { return err } signeddata = signeddata[:n] wire := rawSignatureData(rrset, rr) if wire == nil { return ErrSigGen } signeddata = append(signeddata, wire...) sigbuf := rr.sigBuf() // Get the binary signature data if rr.Algorithm == PRIVATEDNS { // PRIVATEOID // TODO(mg) // remove the domain name and assume its our } 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 } // Setup the hash as defined for this alg. var h hash.Hash var ch crypto.Hash switch rr.Algorithm { case RSAMD5: h = md5.New() ch = crypto.MD5 case RSASHA1, RSASHA1NSEC3SHA1: h = sha1.New() ch = crypto.SHA1 case RSASHA256: h = sha256.New() ch = crypto.SHA256 case RSASHA512: h = sha512.New() ch = crypto.SHA512 } io.WriteString(h, string(signeddata)) sighash := h.Sum(nil) return rsa.VerifyPKCS1v15(pubkey, ch, sighash, sigbuf) case ECDSAP256SHA256, ECDSAP384SHA384: pubkey := k.publicKeyCurve() if pubkey == nil { return ErrKey } var h hash.Hash switch rr.Algorithm { case ECDSAP256SHA256: h = sha256.New() case ECDSAP384SHA384: h = sha512.New() } io.WriteString(h, string(signeddata)) sighash := h.Sum(nil) // Split sigbuf into the r and s coordinates r := big.NewInt(0) r.SetBytes(sigbuf[:len(sigbuf)/2]) s := big.NewInt(0) s.SetBytes(sigbuf[len(sigbuf)/2:]) if ecdsa.Verify(pubkey, sighash, r, s) { return ErrSig } return nil } // Unknown alg return ErrAlg } // ValidityPeriod uses RFC1982 serial arithmetic to calculate // if a signature period is valid. func (rr *RRSIG) ValidityPeriod() bool { utc := time.Now().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 (s *RRSIG) sigBuf() []byte { sigbuf, err := packBase64([]byte(s.Signature)) if err != nil { return nil } return sigbuf } // setPublicKeyInPrivate sets the public key in the private key. func (k *DNSKEY) setPublicKeyInPrivate(p PrivateKey) bool { switch t := p.(type) { case *dsa.PrivateKey: x := k.publicKeyDSA() if x == nil { return false } t.PublicKey = *x case *rsa.PrivateKey: x := k.publicKeyRSA() if x == nil { return false } t.PublicKey = *x case *ecdsa.PrivateKey: x := k.publicKeyCurve() if x == nil { return false } t.PublicKey = *x } return true } // publicKeyRSA returns the RSA public key from a DNSKEY record. func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey { keybuf, err := packBase64([]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 } // publicKeyCurve returns the Curve public key from the DNSKEY record. func (k *DNSKEY) publicKeyCurve() *ecdsa.PublicKey { keybuf, err := packBase64([]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 := packBase64([]byte(k.PublicKey)) if err != nil { return nil } if len(keybuf) < 22 { // TODO: check return nil } t := int(keybuf[0]) size := 64 + t*8 pubkey := new(dsa.PublicKey) pubkey.Parameters.Q = big.NewInt(0) pubkey.Parameters.Q.SetBytes(keybuf[1:21]) // +/- 1 ? pubkey.Parameters.P = big.NewInt(0) pubkey.Parameters.P.SetBytes(keybuf[22 : 22+size]) pubkey.Parameters.G = big.NewInt(0) pubkey.Parameters.G.SetBytes(keybuf[22+size+1 : 22+size*2]) pubkey.Y = big.NewInt(0) pubkey.Y.SetBytes(keybuf[22+size*2+1 : 22+size*3]) return pubkey } // 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 = unpackBase64(buf) return true } // Set the public key for Elliptic Curves func (k *DNSKEY) setPublicKeyCurve(_X, _Y *big.Int) bool { if _X == nil || _Y == nil { return false } buf := curveToBuf(_X, _Y) // Check the length of the buffer, either 64 or 92 bytes k.PublicKey = unpackBase64(buf) 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 = unpackBase64(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) []byte { buf := _X.Bytes() buf = append(buf, _Y.Bytes()...) 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 := byte((len(_G.Bytes()) - 64) / 8) buf := []byte{t} buf = append(buf, _Q.Bytes()...) buf = append(buf, _P.Bytes()...) buf = append(buf, _G.Bytes()...) buf = append(buf, _Y.Bytes()...) return buf } type wireSlice [][]byte func (p wireSlice) Len() int { return len(p) } 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 } func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Return the raw signature data. func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte) { 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 switch x := r.(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, r.len()*2) // TODO(mg): *2 ? off, err1 := PackRR(r1, wire, 0, nil, false) if err1 != nil { return nil } wire = wire[:off] wires[i] = wire } sort.Sort(wires) for _, wire := range wires { buf = append(buf, wire...) } return } // 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 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) golang-dns-0.0~git20130912/dnssec_test.go000066400000000000000000000321001221430110200177510ustar00rootroot00000000000000// 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. package dns import ( "crypto/rsa" "strings" "testing" ) 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) { 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.Logf("%s\n", key.String()) t.Logf("%s\n", key.PrivateKeyString(privkey)) } func TestGenerateDSA(t *testing.T) { 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.Logf("%s\n", key.String()) t.Logf("%s\n", key.PrivateKeyString(privkey)) } func TestGenerateRSA(t *testing.T) { 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.Logf("%s\n", key.String()) t.Logf("%s\n", 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 seperately, so this will keep on working if sig.Verify(key, []RR{soa}) != nil { t.Log("Failure to validate") t.Fail() } } 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() { t.Log("Should not be valid") t.Fail() } 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() { t.Log("Should be valid") t.Fail() } } 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 // 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)) 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} { if sig.Sign(privkey, []RR{r}) != nil { t.Log("Failure to sign the SOA record") t.Fail() continue } if sig.Verify(key, []RR{r}) != nil { t.Log("Failure to validate") t.Fail() continue } t.Logf("Validated: %s\n", r.Header().Name) } } func TestDnskey(t *testing.T) { // f, _ := os.Open("t/Kmiek.nl.+010+05240.key") pubkey, _ := 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") privkey, _ := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(` Private-key-format: v1.2 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== `), "Kmiek.nl.+010+05240.private") if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" { t.Log("Pubkey is not what we've read") t.Fail() } // Coefficient looks fishy... t.Logf("%s", 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.Logf("Wrong key tag: %d for key %v\n", tag, key) t.Fail() } } func TestKeyRSA(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 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, []RR{soa}); err != nil { t.Logf("Failed to sign") t.Fail() return } if err := sig.Verify(key, []RR{soa}); err != nil { t.Logf("Failed to verify") t.Fail() } } 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.Logf("Wrong DS digest for SHA1\n%v\n", ds) t.Fail() } } 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.Logf("%v\n", err) t.Fail() } switch priv := p.(type) { case *rsa.PrivateKey: if 65537 != priv.PublicKey.E { t.Log("Exponenent should be 65537") t.Fail() } default: t.Logf("We should have read an RSA key: %v", priv) t.Fail() } if k.KeyTag() != 37350 { t.Logf("%d %v\n", k.KeyTag(), k) t.Log("Keytag should be 37350") t.Fail() } 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, []RR{soa}) if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" { t.Log("Signature is not correct") t.Logf("%v\n", sig) t.Fail() } } func TestSignECDSA(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.Error()) } privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv) if err != nil { t.Fatal(err.Error()) } 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 sig.Sign(privkey, []RR{a}) t.Logf("%s", sig.String()) if e := sig.Verify(eckey.(*DNSKEY), []RR{a}); e != nil { t.Logf("Failure to validate: %s", e.Error()) t.Fail() } } golang-dns-0.0~git20130912/dyn_test.go000066400000000000000000000003021221430110200172630ustar00rootroot00000000000000// 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. package dns // Find better solution golang-dns-0.0~git20130912/edns.go000066400000000000000000000303071221430110200163730ustar00rootroot00000000000000// 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. // EDNS0 // // EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated // by RFC 6891. It defines a standard 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 interfaces. Currently // only a few have been standardized: EDNS0_NSID (RFC 5001) and EDNS0_SUBNET (draft). 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. // } // } package dns import ( "encoding/hex" "errors" "net" "strconv" ) // EDNS0 Option codes. const ( EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt EDNS0NSID = 0x3 // nsid (RFC5001) EDNS0SUBNET = 0x50fa // client-subnet draft: http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-01 EDNS0DAU = 0x5 // DNSSEC Algorithm Understood EDNS0DHU = 0x6 // DS Hash Understood EDNS0N3U = 0x7 // NSEC3 Hash Understood _DO = 1 << 7 // dnssec ok ) type OPT struct { Hdr RR_Header Option []EDNS0 `dns:"opt"` } func (rr *OPT) Header() *RR_Header { return &rr.Hdr } func (rr *OPT) String() string { s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; " if rr.Do() { s += "flags: do; " } else { s += "flags: ; " } s += "udp: " + strconv.Itoa(int(rr.UDPSize())) for _, o := range rr.Option { switch o.(type) { case *EDNS0_NSID: s += "\n; NSID: " + o.String() h, e := o.pack() var r string if e == nil { for _, c := range h { r += "(" + string(c) + ")" } s += " " + r } case *EDNS0_SUBNET: s += "\n; SUBNET: " + o.String() case *EDNS0_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() } } return s } func (rr *OPT) len() int { l := rr.Hdr.len() for i := 0; i < len(rr.Option); i++ { lo, _ := rr.Option[i].pack() l += 2 + len(lo) } return l } func (rr *OPT) copy() RR { return &OPT{*rr.Hdr.copyHeader(), rr.Option} } // return the old value -> delete SetVersion // Version returns the EDNS version used. Only zero is defined. func (rr *OPT) Version() uint8 { return uint8(rr.Hdr.Ttl & 0x00FF00FFFF) } // 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) } // 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 byte(rr.Hdr.Ttl>>8)&_DO == _DO } // SetDo sets the DO (DNSSEC OK) bit. func (rr *OPT) SetDo() { b1 := byte(rr.Hdr.Ttl >> 24) b2 := byte(rr.Hdr.Ttl >> 16) b3 := byte(rr.Hdr.Ttl >> 8) b4 := byte(rr.Hdr.Ttl) b3 |= _DO // Set it rr.Hdr.Ttl = uint32(b1)<<24 | uint32(b2)<<16 | uint32(b3)<<8 | uint32(b4) } // EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to // it. Basic use pattern for adding an option to and OPT RR: // // // o is the OPT RR, e is the EDNS0 option // o.Option = append(o.Option, e) 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) // String returns the string representation of the option. String() string } // The nsid EDNS0 option is used to retrieve some sort of nameserver // identifier. When seding 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 // 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) Option() uint16 { return EDNS0NSID } 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) unpack(b []byte) { e.Nsid = hex.EncodeToString(b) } func (e *EDNS0_NSID) String() string { return string(e.Nsid) } // The subnet EDNS0 option is used to give the remote nameserver // an idea of where the client lives. It can then give back a different // answer depending on the location or network topology. // Basic use pattern for creating an subnet option: // // o := new(dns.OPT) // o.Hdr.Name = "." // o.Hdr.Rrtype = dns.TypeOPT // e := new(dns.EDNS0_SUBNET) // e.Code = dns.EDNS0SUBNET // e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 // e.NetMask = 32 // 32 for IPV4, 128 for IPv6 // e.SourceScope = 0 // e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 // // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 // o.Option = append(o.Option, e) type EDNS0_SUBNET struct { Code uint16 // Always EDNS0SUBNET Family uint16 // 1 for IP, 2 for IP6 SourceNetmask uint8 SourceScope uint8 Address net.IP } func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET } func (e *EDNS0_SUBNET) pack() ([]byte, error) { b := make([]byte, 4) b[0], b[1] = packUint16(e.Family) b[2] = e.SourceNetmask b[3] = e.SourceScope switch e.Family { case 1: if e.SourceNetmask > net.IPv4len*8 { return nil, errors.New("dns: bad netmask") } ip := make([]byte, net.IPv4len) a := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) for i := 0; i < net.IPv4len; i++ { if i+1 > len(e.Address) { break } ip[i] = a[i] } needLength := e.SourceNetmask / 8 if e.SourceNetmask%8 > 0 { needLength++ } ip = ip[:needLength] b = append(b, ip...) case 2: if e.SourceNetmask > net.IPv6len*8 { return nil, errors.New("dns: bad netmask") } ip := make([]byte, net.IPv6len) a := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) for i := 0; i < net.IPv6len; i++ { if i+1 > len(e.Address) { break } ip[i] = a[i] } needLength := e.SourceNetmask / 8 if e.SourceNetmask%8 > 0 { needLength++ } ip = ip[:needLength] b = append(b, ip...) default: return nil, errors.New("dns: bad address family") } return b, nil } func (e *EDNS0_SUBNET) unpack(b []byte) { lb := len(b) if lb < 4 { return } e.Family, _ = unpackUint16(b, 0) e.SourceNetmask = b[2] e.SourceScope = b[3] switch e.Family { case 1: addr := make([]byte, 4) for i := 0; i < int(e.SourceNetmask/8); i++ { if 4+i > len(b) { break } addr[i] = b[4+i] } e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3]) case 2: addr := make([]byte, 16) for i := 0; i < int(e.SourceNetmask/8); i++ { if 4+i > len(b) { break } 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]} } return } 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 UL (Update Lease) EDNS0 (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 } // Copied: http://golang.org/src/pkg/net/dnsmsg.go func (e *EDNS0_UL) pack() ([]byte, error) { b := make([]byte, 4) b[0] = byte(e.Lease >> 24) b[1] = byte(e.Lease >> 16) b[2] = byte(e.Lease >> 8) b[3] = byte(e.Lease) return b, nil } func (e *EDNS0_UL) unpack(b []byte) { e.Lease = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) } func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) } // Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 // Implemented for completeness, as the EDNS0 type code is assigned. type EDNS0_LLQ struct { Code uint16 // Always EDNS0LLQ Version uint16 Opcode uint16 Error uint16 Id uint64 LeaseLife uint32 } func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ } func (e *EDNS0_LLQ) pack() ([]byte, error) { b := make([]byte, 18) b[0], b[1] = packUint16(e.Version) b[2], b[3] = packUint16(e.Opcode) b[4], b[5] = packUint16(e.Error) b[6] = byte(e.Id >> 56) b[7] = byte(e.Id >> 48) b[8] = byte(e.Id >> 40) b[9] = byte(e.Id >> 32) b[10] = byte(e.Id >> 24) b[11] = byte(e.Id >> 16) b[12] = byte(e.Id >> 8) b[13] = byte(e.Id) b[14] = byte(e.LeaseLife >> 24) b[15] = byte(e.LeaseLife >> 16) b[16] = byte(e.LeaseLife >> 8) b[17] = byte(e.LeaseLife) return nil, nil } func (e *EDNS0_LLQ) unpack(b []byte) { e.Version, _ = unpackUint16(b, 0) e.Opcode, _ = unpackUint16(b, 2) e.Error, _ = unpackUint16(b, 4) e.Id = uint64(b[6])<<56 | uint64(b[6+1])<<48 | uint64(b[6+2])<<40 | uint64(b[6+3])<<32 | uint64(b[6+4])<<24 | uint64(b[6+5])<<16 | uint64(b[6+6])<<8 | uint64(b[6+7]) e.LeaseLife = uint32(b[14])<<24 | uint32(b[14+1])<<16 | uint32(b[14+2])<<8 | uint32(b[14+3]) } 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) { e.AlgCode = b } 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) { e.AlgCode = b } 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) { e.AlgCode = b } 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 } golang-dns-0.0~git20130912/ex/000077500000000000000000000000001221430110200155245ustar00rootroot00000000000000golang-dns-0.0~git20130912/ex/as112/000077500000000000000000000000001221430110200163535ustar00rootroot00000000000000golang-dns-0.0~git20130912/ex/as112/as112.go000066400000000000000000000053261221430110200175370ustar00rootroot00000000000000// 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. // An AS112 blackhole DNS server. Similar to the one found in evldns. // Also see https://www.as112.net/ package main import ( "github.com/miekg/dns" "log" "os" "os/signal" "runtime" "syscall" ) const SOA string = "@ SOA prisoner.iana.org. hostmaster.root-servers.org. 2002040800 1800 900 0604800 604800" func NewRR(s string) dns.RR { r, _ := dns.NewRR(s); return r } var zones = map[string]dns.RR{ "10.in-addr.arpa.": NewRR("$ORIGIN 10.in-addr.arpa.\n" + SOA), "254.169.in-addr.arpa.": NewRR("$ORIGIN 254.169.in-addr.arpa.\n" + SOA), "168.192.in-addr.arpa.": NewRR("$ORIGIN 168.192.in-addr.arpa.\n" + SOA), "16.172.in-addr.arpa.": NewRR("$ORIGIN 16.172.in-addr.arpa.\n" + SOA), "17.172.in-addr.arpa.": NewRR("$ORIGIN 17.172.in-addr.arpa.\n" + SOA), "18.172.in-addr.arpa.": NewRR("$ORIGIN 18.172.in-addr.arpa.\n" + SOA), "19.172.in-addr.arpa.": NewRR("$ORIGIN 19.172.in-addr.arpa.\n" + SOA), "20.172.in-addr.arpa.": NewRR("$ORIGIN 20.172.in-addr.arpa.\n" + SOA), "21.172.in-addr.arpa.": NewRR("$ORIGIN 21.172.in-addr.arpa.\n" + SOA), "22.172.in-addr.arpa.": NewRR("$ORIGIN 22.172.in-addr.arpa.\n" + SOA), "23.172.in-addr.arpa.": NewRR("$ORIGIN 23.172.in-addr.arpa.\n" + SOA), "24.172.in-addr.arpa.": NewRR("$ORIGIN 24.172.in-addr.arpa.\n" + SOA), "25.172.in-addr.arpa.": NewRR("$ORIGIN 25.172.in-addr.arpa.\n" + SOA), "26.172.in-addr.arpa.": NewRR("$ORIGIN 26.172.in-addr.arpa.\n" + SOA), "27.172.in-addr.arpa.": NewRR("$ORIGIN 27.172.in-addr.arpa.\n" + SOA), "28.172.in-addr.arpa.": NewRR("$ORIGIN 28.172.in-addr.arpa.\n" + SOA), "29.172.in-addr.arpa.": NewRR("$ORIGIN 29.172.in-addr.arpa.\n" + SOA), "30.172.in-addr.arpa.": NewRR("$ORIGIN 30.172.in-addr.arpa.\n" + SOA), "31.172.in-addr.arpa.": NewRR("$ORIGIN 31.172.in-addr.arpa.\n" + SOA), } func main() { runtime.GOMAXPROCS(runtime.NumCPU() * 4) for z, rr := range zones { rrx := rr.(*dns.SOA) // Needed to create the actual RR, and not an reference. dns.HandleFunc(z, func(w dns.ResponseWriter, r *dns.Msg) { m := new(dns.Msg) m.SetReply(r) m.Authoritative = true m.Ns = []dns.RR{rrx} w.WriteMsg(m) }) } go func() { err := dns.ListenAndServe(":8053", "tcp", nil) if err != nil { log.Fatal("Failed to set tcp listener %s\n", err.Error()) } }() go func() { err := dns.ListenAndServe(":8053", "udp", nil) if err != nil { log.Fatal("Failed to set udp listener %s\n", err.Error()) } }() sig := make(chan os.Signal) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) for { select { case s := <-sig: log.Fatalf("Signal (%d) received, stopping\n", s) } } } golang-dns-0.0~git20130912/ex/chaos/000077500000000000000000000000001221430110200166215ustar00rootroot00000000000000golang-dns-0.0~git20130912/ex/chaos/chaos.go000066400000000000000000000043171221430110200202520ustar00rootroot00000000000000// 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. // Chaos is a small program that prints the version.bind and hostname.bind // for each address of the nameserver given as argument. package main import ( "fmt" "github.com/miekg/dns" "net" "os" ) func main() { if len(os.Args) != 2 { fmt.Printf("%s NAMESERVER\n", os.Args[0]) os.Exit(1) } conf, _ := dns.ClientConfigFromFile("/etc/resolv.conf") m := new(dns.Msg) m.Question = make([]dns.Question, 1) c := new(dns.Client) addr := addresses(conf, c, os.Args[1]) if len(addr) == 0 { fmt.Printf("No address found for %s\n", os.Args[1]) os.Exit(1) } for _, a := range addr { m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS} in, rtt, _ := c.Exchange(m, a) if in != nil && len(in.Answer) > 0 { fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0]) } m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS} in, rtt, _ = c.Exchange(m, a) if in != nil && len(in.Answer) > 0 { fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0]) } } } func do(t chan *dns.Msg, c *dns.Client, m *dns.Msg, addr string) { go func() { r, _, err := c.Exchange(m, addr) if err != nil { //print error stuff t <- nil } t <- r }() } func addresses(conf *dns.ClientConfig, c *dns.Client, name string) (ips []string) { m4 := new(dns.Msg) m4.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeA) m6 := new(dns.Msg) m6.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeAAAA) t := make(chan *dns.Msg) defer close(t) do(t, c, m4, net.JoinHostPort(conf.Servers[0], conf.Port)) do(t, c, m6, net.JoinHostPort(conf.Servers[0], conf.Port)) i := 2 // two outstanding queries forever: for { select { case d := <-t: i-- if d == nil { continue } if i == 0 { break forever } if d.Rcode == dns.RcodeSuccess { for _, a := range d.Answer { switch a.(type) { case *dns.A: ips = append(ips, net.JoinHostPort(a.(*dns.A).A.String(), "53")) case *dns.AAAA: ips = append(ips, net.JoinHostPort(a.(*dns.AAAA).AAAA.String(), "53")) } } } } } return ips } golang-dns-0.0~git20130912/ex/q/000077500000000000000000000000001221430110200157645ustar00rootroot00000000000000golang-dns-0.0~git20130912/ex/q/q.go000066400000000000000000000251221221430110200165550ustar00rootroot00000000000000// 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. // Q is a small utility which acts and behaves like 'dig' from BIND. // It is meant to stay lean and mean, while having a bunch of handy // features, like -check which checks if a packet is correctly signed (without // checking the chain of trust). package main import ( "flag" "fmt" "github.com/miekg/dns" "net" "os" "strconv" "strings" "time" ) // TODO: serial in ixfr var ( dnskey *dns.DNSKEY short *bool ) func main() { short = flag.Bool("short", false, "abbreviate long DNSSEC records") dnssec := flag.Bool("dnssec", false, "request DNSSEC records") query := flag.Bool("question", false, "show question") check := flag.Bool("check", false, "check internal DNSSEC consistency") six := flag.Bool("6", false, "use IPv6 only") four := flag.Bool("4", false, "use IPv4 only") anchor := flag.String("anchor", "", "use the DNSKEY in this file for interal DNSSEC consistency") tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key") port := flag.Int("port", 53, "port number to use") aa := flag.Bool("aa", false, "set AA flag in query") ad := flag.Bool("ad", false, "set AD flag in query") cd := flag.Bool("cd", false, "set CD flag in query") rd := flag.Bool("rd", true, "set RD flag in query") fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP") tcp := flag.Bool("tcp", false, "TCP mode") nsid := flag.Bool("nsid", false, "set edns nsid option") client := flag.String("client", "", "set edns client-subnet option") //serial := flag.Int("serial", 0, "perform an IXFR with this serial") flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: %s [options] [@server] [qtype] [qclass] [name ...]\n", os.Args[0]) flag.PrintDefaults() } qtype := uint16(0) qclass := uint16(dns.ClassINET) var qname []string flag.Parse() if *anchor != "" { f, err := os.Open(*anchor) if err != nil { fmt.Fprintf(os.Stderr, "Failure to open %s: %s\n", *anchor, err.Error()) } r, err := dns.ReadRR(f, *anchor) if err != nil { fmt.Fprintf(os.Stderr, "Failure to read an RR from %s: %s\n", *anchor, err.Error()) } if k, ok := r.(*dns.DNSKEY); !ok { fmt.Fprintf(os.Stderr, "No DNSKEY read from %s\n", *anchor) } else { dnskey = k } } var nameserver string Flags: for i := 0; i < flag.NArg(); i++ { // If it starts with @ it is a nameserver if flag.Arg(i)[0] == '@' { nameserver = flag.Arg(i) continue Flags } // First class, then type, to make ANY queries possible // And if it looks like type, it is a type if k, ok := dns.StringToType[strings.ToUpper(flag.Arg(i))]; ok { qtype = k continue Flags } // If it looks like a class, it is a class if k, ok := dns.StringToClass[strings.ToUpper(flag.Arg(i))]; ok { qclass = k continue Flags } // If it starts with TYPExxx it is unknown rr if strings.HasPrefix(flag.Arg(i), "TYPE") { i, e := strconv.Atoi(string([]byte(flag.Arg(i))[4:])) if e == nil { qtype = uint16(i) continue Flags } } // Anything else is a qname qname = append(qname, flag.Arg(i)) } if len(qname) == 0 { qname = make([]string, 1) qname[0] = "." qtype = dns.TypeNS } if qtype == 0 { qtype = dns.TypeA } if len(nameserver) == 0 { conf, err := dns.ClientConfigFromFile("/etc/resolv.conf") if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(2) } nameserver = "@" + conf.Servers[0] } nameserver = string([]byte(nameserver)[1:]) // chop off @ // if the nameserver is from /etc/resolv.conf the [ and ] are already // added, thereby breaking net.ParseIP. Check for this and don't // fully qualify such a name if nameserver[0] == '[' && nameserver[len(nameserver)-1] == ']' { nameserver = nameserver[1 : len(nameserver)-1] } if i := net.ParseIP(nameserver); i != nil { nameserver = net.JoinHostPort(nameserver, strconv.Itoa(*port)) } else { nameserver = dns.Fqdn(nameserver) + ":" + strconv.Itoa(*port) } c := new(dns.Client) if *tcp { c.Net = "tcp" if *four { c.Net = "tcp4" } if *six { c.Net = "tcp6" } } else { c.Net = "udp" if *four { c.Net = "udp4" } if *six { c.Net = "udp6" } } m := new(dns.Msg) m.MsgHdr.Authoritative = *aa m.MsgHdr.AuthenticatedData = *ad m.MsgHdr.CheckingDisabled = *cd m.MsgHdr.RecursionDesired = *rd m.Question = make([]dns.Question, 1) if *dnssec || *nsid || *client != "" { o := new(dns.OPT) o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT if *dnssec { o.SetDo() o.SetUDPSize(dns.DefaultMsgSize) } if *nsid { e := new(dns.EDNS0_NSID) e.Code = dns.EDNS0NSID o.Option = append(o.Option, e) // NSD will not return nsid when the udp message size is too small o.SetUDPSize(dns.DefaultMsgSize) } if *client != "" { e := new(dns.EDNS0_SUBNET) e.Code = dns.EDNS0SUBNET e.SourceScope = 0 e.Address = net.ParseIP(*client) if e.Address == nil { fmt.Fprintf(os.Stderr, "Failure to parse IP address: %s\n", *client) return } e.Family = 1 // IP4 e.SourceNetmask = net.IPv4len * 8 if e.Address.To4() == nil { e.Family = 2 // IP6 e.SourceNetmask = net.IPv6len * 8 } o.Option = append(o.Option, e) } m.Extra = append(m.Extra, o) } for _, v := range qname { m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass} m.Id = dns.Id() // Add tsig if *tsig != "" { if algo, name, secret, ok := tsigKeyParse(*tsig); ok { m.SetTsig(name, algo, 300, time.Now().Unix()) c.TsigSecret = map[string]string{name: secret} } else { fmt.Fprintf(os.Stderr, "TSIG key data error\n") return } } if *query { fmt.Printf("%s", m.String()) fmt.Printf("\n;; size: %d bytes\n\n", m.Len()) } if qtype == dns.TypeAXFR { c.Net = "tcp" doXfr(c, m, nameserver) continue } if qtype == dns.TypeIXFR { doXfr(c, m, nameserver) continue } r, rtt, e := c.Exchange(m, nameserver) Redo: if e != nil { fmt.Printf(";; %s\n", e.Error()) continue } if r.Id != m.Id { fmt.Fprintf(os.Stderr, "Id mismatch\n") return } if r.MsgHdr.Truncated && *fallback { if c.Net != "tcp" { if !*dnssec { fmt.Printf(";; Truncated, trying %d bytes bufsize\n", dns.DefaultMsgSize) o := new(dns.OPT) o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT o.SetUDPSize(dns.DefaultMsgSize) m.Extra = append(m.Extra, o) r, rtt, e = c.Exchange(m, nameserver) *dnssec = true goto Redo } else { // First EDNS, then TCP fmt.Printf(";; Truncated, trying TCP\n") c.Net = "tcp" r, rtt, e = c.Exchange(m, nameserver) goto Redo } } } if r.MsgHdr.Truncated && !*fallback { fmt.Printf(";; Truncated\n") } if *check { sigCheck(r, nameserver, *tcp) } if *short { r = shortMsg(r) } fmt.Printf("%v", r) fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, c.Net, r.Len()) } } func tsigKeyParse(s string) (algo, name, secret string, ok bool) { s1 := strings.SplitN(s, ":", 3) switch len(s1) { case 2: return "hmac-md5.sig-alg.reg.int.", s1[0], s1[1], true case 3: switch s1[0] { case "hmac-md5": return "hmac-md5.sig-alg.reg.int.", s1[1], s1[2], true case "hmac-sha1": return "hmac-sha1.", s1[1], s1[2], true case "hmac-sha256": return "hmac-sha256.", s1[1], s1[2], true } } return } func sectionCheck(set []dns.RR, server string, tcp bool) { var key *dns.DNSKEY for _, rr := range set { if rr.Header().Rrtype == dns.TypeRRSIG { rrset := getRRset(set, rr.Header().Name, rr.(*dns.RRSIG).TypeCovered) if dnskey == nil { key = getKey(rr.(*dns.RRSIG).SignerName, rr.(*dns.RRSIG).KeyTag, server, tcp) } else { key = dnskey } if key == nil { fmt.Printf(";? DNSKEY %s/%d not found\n", rr.(*dns.RRSIG).SignerName, rr.(*dns.RRSIG).KeyTag) continue } where := "net" if dnskey != nil { where = "disk" } if err := rr.(*dns.RRSIG).Verify(key, rrset); err != nil { fmt.Printf(";- Bogus signature, %s does not validate (DNSKEY %s/%d/%s) [%s]\n", shortSig(rr.(*dns.RRSIG)), key.Header().Name, key.KeyTag(), where, err.Error()) } else { fmt.Printf(";+ Secure signature, %s validates (DNSKEY %s/%d/%s)\n", shortSig(rr.(*dns.RRSIG)), key.Header().Name, key.KeyTag(), where) } } } } // Check the sigs in the msg, get the signer's key (additional query), get the // rrset from the message, check the signature(s) func sigCheck(in *dns.Msg, server string, tcp bool) { sectionCheck(in.Answer, server, tcp) sectionCheck(in.Ns, server, tcp) sectionCheck(in.Extra, server, tcp) } // Return the RRset belonging to the signature with name and type t func getRRset(l []dns.RR, name string, t uint16) []dns.RR { l1 := make([]dns.RR, 0) for _, rr := range l { if strings.ToLower(rr.Header().Name) == strings.ToLower(name) && rr.Header().Rrtype == t { l1 = append(l1, rr) } } return l1 } // Get the key from the DNS (uses the local resolver) and return them. // If nothing is found we return nil func getKey(name string, keytag uint16, server string, tcp bool) *dns.DNSKEY { c := new(dns.Client) if tcp { c.Net = "tcp" } m := new(dns.Msg) m.SetQuestion(name, dns.TypeDNSKEY) m.SetEdns0(4096, true) r, _, err := c.Exchange(m, server) if err != nil { return nil } for _, k := range r.Answer { if k1, ok := k.(*dns.DNSKEY); ok { if k1.KeyTag() == keytag { return k1 } } } return nil } // shorten RRSIG to "miek.nl RRSIG(NS)" func shortSig(sig *dns.RRSIG) string { return sig.Header().Name + " RRSIG(" + dns.TypeToString[sig.TypeCovered] + ")" } // Walk trough message and short Key data and Sig data func shortMsg(in *dns.Msg) *dns.Msg { for i := 0; i < len(in.Answer); i++ { in.Answer[i] = shortRR(in.Answer[i]) } for i := 0; i < len(in.Ns); i++ { in.Ns[i] = shortRR(in.Ns[i]) } for i := 0; i < len(in.Extra); i++ { in.Extra[i] = shortRR(in.Extra[i]) } return in } func shortRR(r dns.RR) dns.RR { switch t := r.(type) { case *dns.DS: t.Digest = "..." case *dns.DNSKEY: t.PublicKey = "..." case *dns.RRSIG: t.Signature = "..." case *dns.NSEC3: t.Salt = "." // Nobody cares if len(t.TypeBitMap) > 5 { t.TypeBitMap = t.TypeBitMap[1:5] } } return r } func doXfr(c *dns.Client, m *dns.Msg, nameserver string) { if t, e := c.TransferIn(m, nameserver); e == nil { for r := range t { if r.Error == nil { for _, rr := range r.RR { if *short { rr = shortRR(rr) } fmt.Printf("%v\n", rr) } } else { fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", r.Error.Error()) } } } else { fmt.Fprintf(os.Stderr, "Failure to read XFR: %s\n", e.Error()) } } golang-dns-0.0~git20130912/ex/reflect/000077500000000000000000000000001221430110200171505ustar00rootroot00000000000000golang-dns-0.0~git20130912/ex/reflect/reflect.go000066400000000000000000000117571221430110200211360ustar00rootroot00000000000000// 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. // Reflect is a small name server which sends back the IP address of its client, the // recursive resolver. // When queried for type A (resp. AAAA), it sends back the IPv4 (resp. v6) address. // In the additional section the port number and transport are shown. // // Basic use pattern: // // dig @localhost -p 8053 whoami.miek.nl A // // ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2157 // ;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 // ;; QUESTION SECTION: // ;whoami.miek.nl. IN A // // ;; ANSWER SECTION: // whoami.miek.nl. 0 IN A 127.0.0.1 // // ;; ADDITIONAL SECTION: // whoami.miek.nl. 0 IN TXT "Port: 56195 (udp)" // // Similar services: whoami.ultradns.net, whoami.akamai.net. Also (but it // is not their normal goal): rs.dns-oarc.net, porttest.dns-oarc.net, // amiopen.openresolvers.org. // // Original version is from: Stephane Bortzmeyer . // // Adapted to Go (i.e. completely rewritten) by Miek Gieben . package main import ( "flag" "fmt" "github.com/miekg/dns" "log" "net" "os" "os/signal" "runtime" "runtime/pprof" "strconv" "strings" "syscall" "time" ) var ( printf *bool compress *bool tsig *string ) const dom = "whoami.miek.nl." func handleReflect(w dns.ResponseWriter, r *dns.Msg) { var ( v4 bool rr dns.RR str string a net.IP ) // TC must be done here m := new(dns.Msg) m.SetReply(r) m.Compress = *compress if ip, ok := w.RemoteAddr().(*net.UDPAddr); ok { str = "Port: " + strconv.Itoa(ip.Port) + " (udp)" a = ip.IP v4 = a.To4() != nil } if ip, ok := w.RemoteAddr().(*net.TCPAddr); ok { str = "Port: " + strconv.Itoa(ip.Port) + " (tcp)" a = ip.IP v4 = a.To4() != nil } /* if o := r.IsEdns0(); o != nil { for _, s := range o.Option { switch e := s.(type) { case *dns.EDNS0_SUBNET: log.Printf("Edns0 subnet %s", e.Address) } } } */ if v4 { rr = new(dns.A) rr.(*dns.A).Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} rr.(*dns.A).A = a.To4() } else { rr = new(dns.AAAA) rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0} rr.(*dns.AAAA).AAAA = a } t := new(dns.TXT) t.Hdr = dns.RR_Header{Name: dom, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0} t.Txt = []string{str} switch r.Question[0].Qtype { case dns.TypeAXFR: c := make(chan *dns.Envelope) var e *error if err := dns.TransferOut(w, r, c, e); err != nil { close(c) return } soa, _ := dns.NewRR(`whoami.miek.nl. IN SOA elektron.atoom.net. miekg.atoom.net. ( 2009032802 21600 7200 604800 3600)`) c <- &dns.Envelope{RR: []dns.RR{soa, t, rr, soa}} close(c) w.Hijack() // w.Close() // Client closes return case dns.TypeTXT: m.Answer = append(m.Answer, t) m.Extra = append(m.Extra, rr) default: fallthrough case dns.TypeAAAA, dns.TypeA: m.Answer = append(m.Answer, rr) m.Extra = append(m.Extra, t) } if r.IsTsig() != nil { if w.TsigStatus() == nil { m.SetTsig(r.Extra[len(r.Extra)-1].(*dns.TSIG).Hdr.Name, dns.HmacMD5, 300, time.Now().Unix()) } else { println("Status", w.TsigStatus().Error()) } } if *printf { fmt.Printf("%v\n", m.String()) } w.WriteMsg(m) } func serve(net, name, secret string) { switch name { case "": err := dns.ListenAndServe(":8053", net, nil) if err != nil { fmt.Printf("Failed to setup the "+net+" server: %s\n", err.Error()) } default: server := &dns.Server{Addr: ":8053", Net: net, TsigSecret: map[string]string{name: secret}} err := server.ListenAndServe() if err != nil { fmt.Printf("Failed to setup the "+net+" server: %s\n", err.Error()) } } } func main() { runtime.GOMAXPROCS(runtime.NumCPU()*4) cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") printf = flag.Bool("print", false, "print replies") compress = flag.Bool("compress", false, "compress replies") tsig = flag.String("tsig", "", "use MD5 hmac tsig: keyname:base64") var name, secret string flag.Usage = func() { flag.PrintDefaults() } flag.Parse() if *tsig != "" { a := strings.SplitN(*tsig, ":", 2) name, secret = dns.Fqdn(a[0]), a[1] // fqdn the name, which everybody forgets... } if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } dns.HandleFunc("miek.nl.", handleReflect) dns.HandleFunc("authors.bind.", dns.HandleAuthors) dns.HandleFunc("authors.server.", dns.HandleAuthors) dns.HandleFunc("version.bind.", dns.HandleVersion) dns.HandleFunc("version.server.", dns.HandleVersion) go serve("tcp", name, secret) go serve("udp", name, secret) sig := make(chan os.Signal) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) forever: for { select { case s := <-sig: fmt.Printf("Signal (%d) received, stopping\n", s) break forever } } } golang-dns-0.0~git20130912/example_test.go000066400000000000000000000033061221430110200201330ustar00rootroot00000000000000// 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. package dns import ( "fmt" ) // Retrieve the MX records for miek.nl. func ExampleMX() { config, _ := ClientConfigFromFile("/etc/resolv.conf") c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeMX) m.RecursionDesired = true r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port) if err != nil { return } if r.Rcode != RcodeSuccess { return } for _, a := range r.Answer { if mx, ok := a.(*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(zone string) { config, _ := ClientConfigFromFile("/etc/resolv.conf") c := new(Client) m := new(Msg) if zone == "" { zone = "miek.nl" } m.SetQuestion(Fqdn(zone), TypeDNSKEY) m.SetEdns0(4096, true) r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port) if err != nil { return } if r.Rcode != RcodeSuccess { return } for _, k := range r.Answer { if key, ok := k.(*DNSKEY); ok { for _, alg := range []int{SHA1, SHA256, SHA384} { fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags) } } } } // Show how to setup the authors for 'authors.bind. CH TXT' or 'authors.server. CH TXT' // queries. func ExampleAuthors() { // ... server setup is out of scope ... // Register the handle funcs. HandleFunc("authors.bind.", HandleAuthors) HandleFunc("authors.server.", HandleAuthors) // To extend the authors list, just append to dns.Authors (a []string) Authors = append(Authors, "G. I. Joe") // Or ... Authors = []string{"Just Me"} } golang-dns-0.0~git20130912/keygen.go000066400000000000000000000114601221430110200167230ustar00rootroot00000000000000// 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. package dns import ( "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "math/big" "strconv" ) const _FORMAT = "Private-key-format: v1.3\n" // Empty interface that is used as a wrapper around all possible // private key implementations from the crypto package. type PrivateKey interface{} // 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 (r *DNSKEY) Generate(bits int) (PrivateKey, error) { switch r.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 r.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 } r.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 } r.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N) return priv, nil case ECDSAP256SHA256, ECDSAP384SHA384: var c elliptic.Curve switch r.Algorithm { case ECDSAP256SHA256: c = elliptic.P256() case ECDSAP384SHA384: c = elliptic.P384() } priv, err := ecdsa.GenerateKey(c, rand.Reader) if err != nil { return nil, err } r.setPublicKeyCurve(priv.PublicKey.X, priv.PublicKey.Y) return priv, nil default: return nil, ErrAlg } return nil, nil // Dummy return } // 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 (hashing, keytag), so its a method of the DNSKEY. func (r *DNSKEY) PrivateKeyString(p PrivateKey) (s string) { switch t := p.(type) { case *rsa.PrivateKey: algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")" modulus := unpackBase64(t.PublicKey.N.Bytes()) e := big.NewInt(int64(t.PublicKey.E)) publicExponent := unpackBase64(e.Bytes()) privateExponent := unpackBase64(t.D.Bytes()) prime1 := unpackBase64(t.Primes[0].Bytes()) prime2 := unpackBase64(t.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) minusone := big.NewInt(-1) p_1 := big.NewInt(0).Sub(t.Primes[0], one) q_1 := big.NewInt(0).Sub(t.Primes[1], one) exp1 := big.NewInt(0).Mod(t.D, p_1) exp2 := big.NewInt(0).Mod(t.D, q_1) coeff := big.NewInt(0).Exp(t.Primes[1], minusone, t.Primes[0]) exponent1 := unpackBase64(exp1.Bytes()) exponent2 := unpackBase64(exp2.Bytes()) coefficient := unpackBase64(coeff.Bytes()) s = _FORMAT + "Algorithm: " + algorithm + "\n" + "Modules: " + 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: algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")" private := unpackBase64(t.D.Bytes()) s = _FORMAT + "Algorithm: " + algorithm + "\n" + "PrivateKey: " + private + "\n" case *dsa.PrivateKey: algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")" prime := unpackBase64(t.PublicKey.Parameters.P.Bytes()) subprime := unpackBase64(t.PublicKey.Parameters.Q.Bytes()) base := unpackBase64(t.PublicKey.Parameters.G.Bytes()) priv := unpackBase64(t.X.Bytes()) pub := unpackBase64(t.PublicKey.Y.Bytes()) s = _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" } return } golang-dns-0.0~git20130912/kscan.go000066400000000000000000000132531221430110200165420ustar00rootroot00000000000000// 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. package dns import ( "crypto/dsa" "crypto/ecdsa" "crypto/rsa" "io" "math/big" "strings" ) func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) { if s[len(s)-1] != '\n' { // We need a closing newline return k.ReadPrivateKey(strings.NewReader(s+"\n"), "") } return k.ReadPrivateKey(strings.NewReader(s), "") } // NewPrivateKey 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 cryptographics algorithms embed the public inside the privatekey. func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) { m, e := parseKey(q, file) if m == nil { return nil, e } if _, ok := m["private-key-format"]; !ok { return nil, ErrPrivKey } if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" { return nil, ErrPrivKey } // TODO(mg): check if the pubkey matches the private key switch m["algorithm"] { case "3 (DSA)": p, e := readPrivateKeyDSA(m) if e != nil { return nil, e } if !k.setPublicKeyInPrivate(p) { return nil, ErrPrivKey } return p, e case "1 (RSAMD5)": fallthrough case "5 (RSASHA1)": fallthrough case "7 (RSASHA1NSEC3SHA1)": fallthrough case "8 (RSASHA256)": fallthrough case "10 (RSASHA512)": p, e := readPrivateKeyRSA(m) if e != nil { return nil, e } if !k.setPublicKeyInPrivate(p) { return nil, ErrPrivKey } return p, e case "12 (ECC-GOST)": p, e := readPrivateKeyGOST(m) if e != nil { return nil, e } // setPublicKeyInPrivate(p) return p, e case "13 (ECDSAP256SHA256)": fallthrough case "14 (ECDSAP384SHA384)": p, e := readPrivateKeyECDSA(m) if e != nil { return nil, e } if !k.setPublicKeyInPrivate(p) { return nil, ErrPrivKey } return p, e } return nil, ErrPrivKey } // Read a private key (file) string and create a public key. Return the private key. func readPrivateKeyRSA(m map[string]string) (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 := packBase64([]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) (PrivateKey, error) { p := new(dsa.PrivateKey) p.X = big.NewInt(0) for k, v := range m { switch k { case "private_value(x):": v1, err := packBase64([]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) (PrivateKey, error) { p := new(ecdsa.PrivateKey) p.D = big.NewInt(0) // Need to check if we have everything for k, v := range m { switch k { case "privatekey:": v1, err := packBase64([]byte(v)) if err != nil { return nil, err } p.D.SetBytes(v1) case "created:", "publish:", "activate:": /* not used in Go (yet) */ } } return p, nil } func readPrivateKeyGOST(m map[string]string) (PrivateKey, error) { // p := new(ecdsa.PrivateKey) // p.D = big.NewInt(0) // Need to check if we have everything for k, v := range m { switch k { case "gostasn1:": v1, err := packBase64([]byte(v)) if err != nil { return nil, err } v1 = v1 //p.D.SetBytes(v1) case "created:", "publish:", "activate:": /* not used in Go (yet) */ } } return nil, 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 _KEY: k = l.token case _VALUE: 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 = _KEY c <- l // Next token is a space, eat it s.tokenText() key = false str = "" } else { l.value = _VALUE } case ';': commt = true case '\n': if commt { // Reset a comment commt = false } l.value = _VALUE 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 = _VALUE c <- l } } golang-dns-0.0~git20130912/labels.go000066400000000000000000000074221221430110200167060ustar00rootroot00000000000000// 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. package dns // Holds a bunch of helper functions for dealing with labels. // SplitDomainName splits a name string into it's labels. // www.miek.nl. returns []string{"www", "miek", "nl"} // The root label (.) returns nil. func SplitDomainName(s string) (labels []string) { fqdnEnd := 0 // offset of the final '.' or the length of the name idx := Split(s) begin := 0 if s[len(s)-1] == '.' { fqdnEnd = len(s) - 1 } else { fqdnEnd = len(s) } switch len(idx) { case 0: return nil case 1: // no-op default: end := 0 for i := 1; i < len(idx); i++ { end = idx[i] labels = append(labels, s[begin:end-1]) begin = end } } labels = append(labels, s[begin:fqdnEnd]) return labels } // CompareDomainName compares the names s1 and s2 and // returns how many labels they have in common starting from the *right*. // The comparison stops at the first inequality. The names are not downcased // before the comparison. // // www.miek.nl. and miek.nl. have two labels in common: miek and nl // www.miek.nl. and www.bla.nl. have one label in common: nl func CompareDomainName(s1, s2 string) (n int) { s1 = Fqdn(s1) s2 = Fqdn(s2) l1 := Split(s1) l2 := Split(s2) // the first check: root label if l1 == nil || l2 == nil { return } j1 := len(l1) - 1 // end i1 := len(l1) - 2 // start j2 := len(l2) - 1 i2 := len(l2) - 2 // the second check can be done here: last/only label // before we fall through into the for-loop below if s1[l1[j1]:] == s2[l2[j2]:] { n++ } else { return } for { if i1 < 0 || i2 < 0 { break } if s1[l1[i1]:l1[j1]] == s2[l2[i2]:l2[j2]] { n++ } else { break } j1-- i1-- j2-- i2-- } return } // CountLabel counts the the number of labels in the string s. func CountLabel(s string) (labels int) { if s == "." { return } off := 0 end := false for { off, end = NextLabel(s, off) labels++ if end { return } } panic("dns: not reached") } // Split splits a name s into its label indexes. // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. // The root name (.) returns nil. func Split(s string) []int { if s == "." { return nil } idx := []int{0} off := 0 end := false for { off, end = NextLabel(s, off) if end { return idx } idx = append(idx, off) } panic("dns: not reached") } // NextLabel returns the index of the start of the next label in the // string s starting at offset. // The bool end is true when the end of the string has been reached. func NextLabel(s string, offset int) (i int, end bool) { quote := false for i = offset; i < len(s)-1; i++ { switch s[i] { case '\\': quote = !quote default: quote = false case '.': if quote { quote = !quote continue } return i + 1, false } } return i + 1, true } // PrevLabel returns the index of the label when starting from the right and // jumping n labels to the left. // The bool start is true when the start of the string has been overshot. func PrevLabel(s string, n int) (i int, start bool) { if n == 0 { return len(s), false } lab := Split(s) if lab == nil { return 0, true } if n > len(lab) { return 0, true } return lab[len(lab)-n], false } func LenLabels(s string) int { println("LenLabels is to be removed in future versions, for the better named CountLabel") return CountLabel(s) } func SplitLabels(s string) []string { println("SplitLabels is to be removed in future versions, for the better named SplitDomainName") return SplitDomainName(s) } func CompareLabels(s1, s2 string) (n int) { println("CompareLabels is to be removed in future versions, for better named CompareDomainName") return CompareDomainName(s1, s2) } golang-dns-0.0~git20130912/labels_test.go000066400000000000000000000113651221430110200177460ustar00rootroot00000000000000// 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. package dns import ( "testing" ) func TestCompareDomainName(t *testing.T) { s1 := "www.miek.nl." s2 := "miek.nl." s3 := "www.bla.nl." s4 := "nl.www.bla." s5 := "nl" s6 := "miek.nl" if CompareDomainName(s1, s2) != 2 { t.Logf("%s with %s should be %d", s1, s2, 2) t.Fail() } if CompareDomainName(s1, s3) != 1 { t.Logf("%s with %s should be %d", s1, s3, 1) t.Fail() } if CompareDomainName(s3, s4) != 0 { t.Logf("%s with %s should be %d", s3, s4, 0) t.Fail() } // Non qualified tests if CompareDomainName(s1, s5) != 1 { t.Logf("%s with %s should be %d", s1, s5, 1) t.Fail() } if CompareDomainName(s1, s6) != 2 { t.Logf("%s with %s should be %d", s1, s5, 2) t.Fail() } if CompareDomainName(s1, ".") != 0 { t.Logf("%s with %s should be %d", s1, s5, 0) t.Fail() } if CompareDomainName(".", ".") != 0 { t.Logf("%s with %s should be %d", ".", ".", 0) t.Fail() } } func TestSplit(t *testing.T) { splitter := map[string]int{ "www.miek.nl.": 3, "www.miek.nl": 3, "www..miek.nl": 4, `www\.miek.nl.`: 2, `www\\.miek.nl.`: 3, ".": 0, "nl.": 1, "nl": 1, "com.": 1, ".com.": 2, } for s, i := range splitter { if x := len(Split(s)); x != i { t.Logf("Labels should be %d, got %d: %s %v\n", i, x, s, Split(s)) t.Fail() } else { t.Logf("%s %v\n", s, Split(s)) } } } func TestSplit2(t *testing.T) { splitter := map[string][]int{ "www.miek.nl.": []int{0, 4, 9}, "www.miek.nl": []int{0, 4, 9}, "nl": []int{0}, } for s, i := range splitter { x := Split(s) switch len(i) { case 1: if x[0] != i[0] { t.Logf("Labels should be %v, got %v: %s\n", i, x, s) t.Fail() } default: if x[0] != i[0] || x[1] != i[1] || x[2] != i[2] { t.Logf("Labels should be %v, got %v: %s\n", i, x, s) t.Fail() } } } } func TestPrevLabel(t *testing.T) { type prev struct { string int } prever := map[prev]int{ prev{"www.miek.nl.", 0}: 12, prev{"www.miek.nl.", 1}: 9, prev{"www.miek.nl.", 2}: 4, prev{"www.miek.nl", 0}: 11, prev{"www.miek.nl", 1}: 9, prev{"www.miek.nl", 2}: 4, prev{"www.miek.nl.", 5}: 0, prev{"www.miek.nl", 5}: 0, prev{"www.miek.nl.", 3}: 0, prev{"www.miek.nl", 3}: 0, } for s, i := range prever { x, ok := PrevLabel(s.string, s.int) if i != x { t.Logf("Label should be %d, got %d, %t: preving %d, %s\n", i, x, ok, s.int, s.string) t.Fail() } } } func TestCountLabel(t *testing.T) { splitter := map[string]int{ "www.miek.nl.": 3, "www.miek.nl": 3, "nl": 1, ".": 0, } for s, i := range splitter { x := CountLabel(s) if x != i { t.Logf("CountLabel should have %d, got %d\n", i, x) t.Fail() } } } func TestSplitDomainName(t *testing.T) { labels := map[string][]string{ "miek.nl": []string{"miek", "nl"}, ".": nil, "www.miek.nl.": []string{"www", "miek", "nl"}, "www.miek.nl": []string{"www", "miek", "nl"}, "www..miek.nl": []string{"www", "", "miek", "nl"}, `www\.miek.nl`: []string{`www\.miek`, "nl"}, `www\\.miek.nl`: []string{`www\\`, "miek", "nl"}, } domainLoop: for domain, splits := range labels { parts := SplitDomainName(domain) if len(parts) != len(splits) { t.Logf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits) t.Fail() continue domainLoop } for i := range parts { if parts[i] != splits[i] { t.Logf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits) t.Fail() continue domainLoop } } } } func TestIsDomainName(t *testing.T) { type ret struct { ok bool lab int } names := map[string]*ret{ "..": &ret{false, 1}, "@.": &ret{true, 1}, "www.example.com": &ret{true, 3}, "www.example.com.": &ret{true, 3}, "mi\\k.nl.": &ret{true, 2}, "mi\\k.nl": &ret{true, 2}, } for d, ok := range names { l, k := IsDomainName(d) if ok.ok != k || ok.lab != l { t.Logf(" got %v %d for %s ", k, l, d) t.Logf("have %v %d for %s ", ok.ok, ok.lab, d) t.Fail() } } } func BenchmarkSplitLabels(b *testing.B) { for i := 0; i < b.N; i++ { Split("www.example.com") } } func BenchmarkLenLabels(b *testing.B) { for i := 0; i < b.N; i++ { CountLabel("www.example.com") } } func BenchmarkCompareLabels(b *testing.B) { for i := 0; i < b.N; i++ { CompareDomainName("www.example.com", "aa.example.com") } } func BenchmarkIsSubDomain(b *testing.B) { for i := 0; i < b.N; i++ { IsSubDomain("www.example.com", "aa.example.com") IsSubDomain("example.com", "aa.example.com") IsSubDomain("miek.nl", "aa.example.com") } } golang-dns-0.0~git20130912/msg.go000066400000000000000000001313551221430110200162350ustar00rootroot00000000000000// 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 // DNS packet assembly, see RFC 1035. Converting from - Unpack() - // and to - Pack() - wire format. // All the packers and unpackers take a (msg []byte, off int) // and return (off1 int, ok bool). If they return ok==false, they // also return off1==len(msg), so that the next unpacker will // also fail. This lets us avoid checks of ok until the end of a // packing sequence. package dns import ( "encoding/base32" "encoding/base64" "encoding/hex" "fmt" "math/rand" "net" "reflect" "strconv" "time" ) const maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer var ( ErrFqdn error = &Error{err: "domain must be fully qualified"} ErrId error = &Error{err: "id mismatch"} ErrRdata error = &Error{err: "bad rdata"} ErrBuf error = &Error{err: "buffer size too small"} ErrShortRead error = &Error{err: "short read"} ErrConn error = &Error{err: "conn holds both UDP and TCP connection"} ErrConnEmpty error = &Error{err: "conn has no connection"} ErrServ error = &Error{err: "no servers could be reached"} ErrKey error = &Error{err: "bad key"} ErrPrivKey error = &Error{err: "bad private key"} ErrKeySize error = &Error{err: "bad key size"} ErrKeyAlg error = &Error{err: "bad key algorithm"} ErrAlg error = &Error{err: "bad algorithm"} ErrTime error = &Error{err: "bad time"} ErrNoSig error = &Error{err: "no signature found"} ErrSig error = &Error{err: "bad signature"} ErrSecret error = &Error{err: "no secrets defined"} ErrSigGen error = &Error{err: "bad signature generation"} ErrAuth error = &Error{err: "bad authentication"} ErrSoa error = &Error{err: "no SOA"} ErrRRset error = &Error{err: "bad rrset"} ) // A manually-unpacked version of (id, bits). // This is in its own struct for easy printing. type MsgHdr struct { Id uint16 Response bool Opcode int Authoritative bool Truncated bool RecursionDesired bool RecursionAvailable bool Zero bool AuthenticatedData bool CheckingDisabled bool Rcode int } // The layout of a DNS message. type Msg struct { MsgHdr Compress bool `json:"-"` // If true, the message will be compressed when converted to wire format. Question []Question // Holds the RR(s) of the question section. Answer []RR // Holds the RR(s) of the answer section. Ns []RR // Holds the RR(s) of the authority section. Extra []RR // Holds the RR(s) of the additional section. } // Map of strings for each RR wire type. var TypeToString = map[uint16]string{ TypeCNAME: "CNAME", TypeHINFO: "HINFO", TypeTLSA: "TLSA", TypeMB: "MB", TypeMG: "MG", TypeRP: "RP", TypeMD: "MD", TypeMF: "MF", TypeMINFO: "MINFO", TypeMR: "MR", TypeMX: "MX", TypeWKS: "WKS", TypeNS: "NS", TypeNULL: "NULL", TypeAFSDB: "AFSDB", TypeX25: "X25", TypeISDN: "ISDN", TypePTR: "PTR", TypeRT: "RT", TypeSOA: "SOA", TypeTXT: "TXT", TypeSRV: "SRV", TypeATMA: "ATMA", TypeNAPTR: "NAPTR", TypeKX: "KX", TypeCERT: "CERT", TypeDNAME: "DNAME", TypeA: "A", TypeAAAA: "AAAA", TypeLOC: "LOC", TypeOPT: "OPT", TypeDS: "DS", TypeDHCID: "DHCID", TypeHIP: "HIP", TypeNINFO: "NINFO", TypeRKEY: "RKEY", TypeCDS: "CDS", TypeCAA: "CAA", TypeIPSECKEY: "IPSECKEY", TypeSSHFP: "SSHFP", TypeRRSIG: "RRSIG", TypeNSEC: "NSEC", TypeDNSKEY: "DNSKEY", TypeNSEC3: "NSEC3", TypeNSEC3PARAM: "NSEC3PARAM", TypeTALINK: "TALINK", TypeSPF: "SPF", TypeNID: "NID", TypeL32: "L32", TypeL64: "L64", TypeLP: "LP", TypeUINFO: "UINFO", TypeUID: "UID", TypeGID: "GID", TypeUNSPEC: "UNSPEC", TypeEUI48: "EUI48", TypeEUI64: "EUI64", TypeTKEY: "TKEY", // Meta RR TypeTSIG: "TSIG", // Meta RR TypeAXFR: "AXFR", // Meta RR TypeIXFR: "IXFR", // Meta RR TypeANY: "ANY", // Meta RR TypeURI: "URI", TypeTA: "TA", TypeDLV: "DLV", } // Reverse, needed for string parsing. var StringToType = reverseInt16(TypeToString) var StringToClass = reverseInt16(ClassToString) // Map of opcodes strings. var StringToOpcode = reverseInt(OpcodeToString) // Map of rcodes strings. var StringToRcode = reverseInt(RcodeToString) // Map of strings for each CLASS wire type. var ClassToString = map[uint16]string{ ClassINET: "IN", ClassCSNET: "CS", ClassCHAOS: "CH", ClassHESIOD: "HS", ClassNONE: "NONE", ClassANY: "ANY", } // Map of strings for opcodes. var OpcodeToString = map[int]string{ OpcodeQuery: "QUERY", OpcodeIQuery: "IQUERY", OpcodeStatus: "STATUS", OpcodeNotify: "NOTIFY", OpcodeUpdate: "UPDATE", } // Map of strings for rcodes. var RcodeToString = map[int]string{ RcodeSuccess: "NOERROR", RcodeFormatError: "FORMERR", RcodeServerFailure: "SERVFAIL", RcodeNameError: "NXDOMAIN", RcodeNotImplemented: "NOTIMPL", RcodeRefused: "REFUSED", RcodeYXDomain: "YXDOMAIN", // From RFC 2136 RcodeYXRrset: "YXRRSET", RcodeNXRrset: "NXRRSET", RcodeNotAuth: "NOTAUTH", RcodeNotZone: "NOTZONE", RcodeBadSig: "BADSIG", // Also known as RcodeBadVers, see RFC 6891 // RcodeBadVers: "BADVERS", RcodeBadKey: "BADKEY", RcodeBadTime: "BADTIME", RcodeBadMode: "BADMODE", RcodeBadName: "BADNAME", RcodeBadAlg: "BADALG", RcodeBadTrunc: "BADTRUNC", } // Rather than write the usual handful of routines to pack and // unpack every message that can appear on the wire, we use // reflection to write a generic pack/unpack for structs and then // use it. Thus, if in the future we need to define new message // structs, no new pack/unpack/printing code needs to be written. // Domain names are a sequence of counted strings // split at the dots. They end with a zero-length string. // PackDomainName packs a domain name s into msg[off:]. // If compression is wanted compress must be true and the compression // map needs to hold a mapping between domain names and offsets // pointing into msg[]. func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { off1, _, err = packDomainName(s, msg, off, compression, compress) return } func packDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, labels int, err error) { // special case if msg == nil lenmsg := 256 if msg != nil { lenmsg = len(msg) } ls := len(s) if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. return off, 0, nil } // If not fully qualified, error out, but only if msg == nil #ugly switch { case msg == nil: if s[ls-1] != '.' { s += "." ls++ } case msg != nil: if s[ls-1] != '.' { return lenmsg, 0, ErrFqdn } } // Each dot ends a segment of the name. // We trade each dot byte for a length byte. // Except for escaped dots (\.), which are normal dots. // There is also a trailing zero. // Compression nameoffset := -1 pointer := -1 // Emit sequence of counted strings, chopping at dots. begin := 0 bs := []byte(s) for i := 0; i < ls; i++ { if bs[i] == '\\' { for j := i; j < ls-1; j++ { bs[j] = bs[j+1] } ls-- if off+1 > lenmsg { return lenmsg, labels, ErrBuf } // check for \DDD if i+2 < ls && bs[i] >= '0' && bs[i] <= '9' && bs[i+1] >= '0' && bs[i+1] <= '9' && bs[i+2] >= '0' && bs[i+2] <= '9' { bs[i] = byte((bs[i]-'0')*100 + (bs[i+1]-'0')*10 + (bs[i+2] - '0')) for j := i + 1; j < ls-2; j++ { bs[j] = bs[j+2] } ls -= 2 } continue } if bs[i] == '.' { if i > 0 && bs[i-1] == '.' { // two dots back to back is not legal return lenmsg, labels, ErrRdata } if i-begin >= 1<<6 { // top two bits of length must be clear return lenmsg, labels, ErrRdata } // off can already (we're in a loop) be bigger than len(msg) // this happens when a name isn't fully qualified if off+1 > lenmsg { return lenmsg, labels, ErrBuf } if msg != nil { msg[off] = byte(i - begin) } offset := off off++ for j := begin; j < i; j++ { if off+1 > lenmsg { return lenmsg, labels, ErrBuf } if msg != nil { msg[off] = bs[j] } off++ } // Dont try to compress '.' if compression != nil && string(bs[begin:]) != "." { if p, ok := compression[string(bs[begin:])]; !ok { // Only offsets smaller than this can be used. if offset < maxCompressionOffset { compression[string(bs[begin:])] = offset } } else { // The first hit is the longest matching dname // keep the pointer offset we get back and store // the offset of the current name, because that's // where we need to insert the pointer later // If compress is true, we're allowed to compress this dname if pointer == -1 && compress { pointer = p // Where to point to nameoffset = offset // Where to point from break } } } labels++ begin = i + 1 } } // Root label is special if len(bs) == 1 && bs[0] == '.' { return off, labels, nil } // If we did compression and we find something add the pointer here if pointer != -1 { // We have two bytes (14 bits) to put the pointer in // if msg == nil, we will never do compression msg[nameoffset], msg[nameoffset+1] = packUint16(uint16(pointer ^ 0xC000)) off = nameoffset + 1 goto End } if msg != nil { msg[off] = 0 } End: off++ return off, labels, nil } // Unpack a domain name. // In addition to the simple sequences of counted strings above, // domain names are allowed to refer to strings elsewhere in the // packet, to avoid repeating common suffixes when returning // many entries in a single domain. The pointers are marked // by a length byte with the top two bits set. Ignoring those // two bits, that byte and the next give a 14 bit offset from msg[0] // where we should pick up the trail. // Note that if we jump elsewhere in the packet, // we return off1 == the offset after the first pointer we found, // which is where the next record will start. // In theory, the pointers are only allowed to jump backward. // We let them jump anywhere and stop jumping after a while. // UnpackDomainName unpacks a domain name into a string. func UnpackDomainName(msg []byte, off int) (s string, off1 int, err error) { s = "" lenmsg := len(msg) ptr := 0 // number of pointers followed Loop: for { if off >= lenmsg { return "", lenmsg, ErrBuf } c := int(msg[off]) off++ switch c & 0xC0 { case 0x00: if c == 0x00 { // end of name if s == "" { return ".", off, nil } break Loop } // literal string if off+c > lenmsg { return "", lenmsg, ErrBuf } for j := off; j < off+c; j++ { switch { case msg[j] == '.': // literal dots s += "\\." case msg[j] < 32: // unprintable use \DDD fallthrough case msg[j] >= 127: s += fmt.Sprintf("\\%03d", msg[j]) default: s += string(msg[j]) } } s += "." off += c case 0xC0: // pointer to somewhere else in msg. // remember location after first ptr, // since that's how many bytes we consumed. // also, don't follow too many pointers -- // maybe there's a loop. if off >= lenmsg { return "", lenmsg, ErrBuf } c1 := msg[off] off++ if ptr == 0 { off1 = off } if ptr++; ptr > 10 { return "", lenmsg, &Error{err: "too many compression pointers"} } off = (c^0xC0)<<8 | int(c1) default: // 0x80 and 0x40 are reserved return "", lenmsg, ErrRdata } } if ptr == 0 { off1 = off } return s, off1, nil } // Pack a reflect.StructValue into msg. Struct members can only be uint8, uint16, uint32, string, // slices and other (often anonymous) structs. func packStructValue(val reflect.Value, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { lenmsg := len(msg) for i := 0; i < val.NumField(); i++ { switch fv := val.Field(i); fv.Kind() { default: return lenmsg, &Error{err: "bad kind packing"} case reflect.Slice: switch val.Type().Field(i).Tag { default: return lenmsg, &Error{"bad tag packing slice: " + val.Type().Field(i).Tag.Get("dns")} case `dns:"domain-name"`: for j := 0; j < val.Field(i).Len(); j++ { element := val.Field(i).Index(j).String() off, err = PackDomainName(element, msg, off, compression, false && compress) if err != nil { return lenmsg, err } } case `dns:"txt"`: for j := 0; j < val.Field(i).Len(); j++ { element := val.Field(i).Index(j).String() // Counted string: 1 byte length. if len(element) > 255 || off+1+len(element) > lenmsg { return lenmsg, &Error{err: "overflow packing txt"} } msg[off] = byte(len(element)) off++ for i := 0; i < len(element); i++ { msg[off+i] = element[i] } off += len(element) } case `dns:"opt"`: // edns for j := 0; j < val.Field(i).Len(); j++ { element := val.Field(i).Index(j).Interface() b, e := element.(EDNS0).pack() if e != nil { return lenmsg, &Error{err: "overflow packing opt"} } // Option code msg[off], msg[off+1] = packUint16(element.(EDNS0).Option()) // Length msg[off+2], msg[off+3] = packUint16(uint16(len(b))) off += 4 // Actual data copy(msg[off:off+len(b)], b) off += len(b) } case `dns:"a"`: // It must be a slice of 4, even if it is 16, we encode // only the first 4 if off+net.IPv4len > lenmsg { return lenmsg, &Error{err: "overflow packing a"} } switch fv.Len() { case net.IPv6len: msg[off] = byte(fv.Index(12).Uint()) msg[off+1] = byte(fv.Index(13).Uint()) msg[off+2] = byte(fv.Index(14).Uint()) msg[off+3] = byte(fv.Index(15).Uint()) off += net.IPv4len case net.IPv4len: msg[off] = byte(fv.Index(0).Uint()) msg[off+1] = byte(fv.Index(1).Uint()) msg[off+2] = byte(fv.Index(2).Uint()) msg[off+3] = byte(fv.Index(3).Uint()) off += net.IPv4len case 0: // Allowed, for dynamic updates default: return lenmsg, &Error{err: "overflow packing a"} } case `dns:"aaaa"`: if fv.Len() == 0 { break } if fv.Len() > net.IPv6len || off+fv.Len() > lenmsg { return lenmsg, &Error{err: "overflow packing aaaa"} } for j := 0; j < net.IPv6len; j++ { msg[off] = byte(fv.Index(j).Uint()) off++ } case `dns:"wks"`: if off == lenmsg { break // dyn. updates } if val.Field(i).Len() == 0 { break } var bitmapbyte uint16 for j := 0; j < val.Field(i).Len(); j++ { serv := uint16((fv.Index(j).Uint())) bitmapbyte = uint16(serv / 8) if int(bitmapbyte) > lenmsg { return lenmsg, &Error{err: "overflow packing wks"} } bit := uint16(serv) - bitmapbyte*8 msg[bitmapbyte] = byte(1 << (7 - bit)) } off += int(bitmapbyte) case `dns:"nsec"`: // NSEC/NSEC3 // This is the uint16 type bitmap if val.Field(i).Len() == 0 { // Do absolutely nothing break } lastwindow := uint16(0) length := uint16(0) if off+2 > lenmsg { return lenmsg, &Error{err: "overflow packing nsecx"} } for j := 0; j < val.Field(i).Len(); j++ { t := uint16((fv.Index(j).Uint())) window := uint16(t / 256) if lastwindow != window { // New window, jump to the new offset off += int(length) + 3 if off > lenmsg { return lenmsg, &Error{err: "overflow packing nsecx bitmap"} } } length = (t - window*256) / 8 bit := t - (window * 256) - (length * 8) if off+2+int(length) > lenmsg { return lenmsg, &Error{err: "overflow packing nsecx bitmap"} } // Setting the window # msg[off] = byte(window) // Setting the octets length msg[off+1] = byte(length + 1) // Setting the bit value for the type in the right octet msg[off+2+int(length)] |= byte(1 << (7 - bit)) lastwindow = window } off += 2 + int(length) off++ if off > lenmsg { return lenmsg, &Error{err: "overflow packing nsecx bitmap"} } } case reflect.Struct: off, err = packStructValue(fv, msg, off, compression, compress) if err != nil { return lenmsg, err } case reflect.Uint8: if off+1 > lenmsg { return lenmsg, &Error{err: "overflow packing uint8"} } msg[off] = byte(fv.Uint()) off++ case reflect.Uint16: if off+2 > lenmsg { return lenmsg, &Error{err: "overflow packing uint16"} } i := fv.Uint() msg[off] = byte(i >> 8) msg[off+1] = byte(i) off += 2 case reflect.Uint32: if off+4 > lenmsg { return lenmsg, &Error{err: "overflow packing uint32"} } i := fv.Uint() msg[off] = byte(i >> 24) msg[off+1] = byte(i >> 16) msg[off+2] = byte(i >> 8) msg[off+3] = byte(i) off += 4 case reflect.Uint64: switch val.Type().Field(i).Tag { default: if off+8 > lenmsg { return lenmsg, &Error{err: "overflow packing uint64"} } i := fv.Uint() msg[off] = byte(i >> 56) msg[off+1] = byte(i >> 48) msg[off+2] = byte(i >> 40) msg[off+3] = byte(i >> 32) msg[off+4] = byte(i >> 24) msg[off+5] = byte(i >> 16) msg[off+6] = byte(i >> 8) msg[off+7] = byte(i) off += 8 case `dns:"uint48"`: // Used in TSIG, where it stops at 48 bits, so we discard the upper 16 if off+6 > lenmsg { return lenmsg, &Error{err: "overflow packing uint64 as uint48"} } i := fv.Uint() msg[off] = byte(i >> 40) msg[off+1] = byte(i >> 32) msg[off+2] = byte(i >> 24) msg[off+3] = byte(i >> 16) msg[off+4] = byte(i >> 8) msg[off+5] = byte(i) off += 6 } case reflect.String: // There are multiple string encodings. // The tag distinguishes ordinary strings from domain names. s := fv.String() switch val.Type().Field(i).Tag { default: return lenmsg, &Error{"bad tag packing string: " + val.Type().Field(i).Tag.Get("dns")} case `dns:"base64"`: b64, err := packBase64([]byte(s)) if err != nil { return lenmsg, &Error{err: "overflow packing base64"} } copy(msg[off:off+len(b64)], b64) off += len(b64) case `dns:"domain-name"`: if off, err = PackDomainName(s, msg, off, compression, false && compress); err != nil { return lenmsg, err } case `dns:"cdomain-name"`: if off, err = PackDomainName(s, msg, off, compression, true && compress); err != nil { return lenmsg, err } case `dns:"size-base32"`: // This is purely for NSEC3 atm, the previous byte must // holds the length of the encoded string. As NSEC3 // is only defined to SHA1, the hashlength is 20 (160 bits) msg[off-1] = 20 fallthrough case `dns:"base32"`: b32, err := packBase32([]byte(s)) if err != nil { return lenmsg, &Error{err: "overflow packing base32"} } copy(msg[off:off+len(b32)], b32) off += len(b32) case `dns:"size-hex"`: fallthrough case `dns:"hex"`: // There is no length encoded here h, e := hex.DecodeString(s) if e != nil { return lenmsg, &Error{err: "overflow packing hex"} } if off+hex.DecodedLen(len(s)) > lenmsg { return lenmsg, &Error{err: "overflow packing hex"} } copy(msg[off:off+hex.DecodedLen(len(s))], h) off += hex.DecodedLen(len(s)) case `dns:"size"`: // the size is already encoded in the RR, we can safely use the // length of string. String is RAW (not encoded in hex, nor base64) copy(msg[off:off+len(s)], s) off += len(s) case `dns:"txt"`: fallthrough case "": // Counted string: 1 byte length. if len(s) > 255 || off+1+len(s) > lenmsg { return lenmsg, &Error{err: "overflow packing string"} } msg[off] = byte(len(s)) off++ for i := 0; i < len(s); i++ { msg[off+i] = s[i] } off += len(s) } } } return off, nil } func structValue(any interface{}) reflect.Value { return reflect.ValueOf(any).Elem() } // PackStruct packs any structure to wire format. func PackStruct(any interface{}, msg []byte, off int) (off1 int, err error) { off, err = packStructValue(structValue(any), msg, off, nil, false) return off, err } func packStructCompress(any interface{}, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { off, err = packStructValue(structValue(any), msg, off, compression, compress) return off, err } // TODO(mg): Fix use of rdlength here // Unpack a reflect.StructValue from msg. // Same restrictions as packStructValue. func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err error) { var rdstart int lenmsg := len(msg) for i := 0; i < val.NumField(); i++ { switch fv := val.Field(i); fv.Kind() { default: return lenmsg, &Error{err: "bad kind unpacking"} case reflect.Slice: switch val.Type().Field(i).Tag { default: return lenmsg, &Error{"bad tag unpacking slice: " + val.Type().Field(i).Tag.Get("dns")} case `dns:"domain-name"`: // HIP record slice of name (or none) servers := make([]string, 0) var s string for off < lenmsg { s, off, err = UnpackDomainName(msg, off) if err != nil { return lenmsg, err } servers = append(servers, s) } fv.Set(reflect.ValueOf(servers)) case `dns:"txt"`: txt := make([]string, 0) rdlength := off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) Txts: if off == lenmsg { // dyn. updates, no rdata is OK break } l := int(msg[off]) if off+l+1 > lenmsg { return lenmsg, &Error{err: "overflow unpacking txt"} } txt = append(txt, string(msg[off+1:off+l+1])) off += l + 1 if off < rdlength { // More goto Txts } fv.Set(reflect.ValueOf(txt)) case `dns:"opt"`: // edns0 rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) endrr := off + rdlength if rdlength == 0 { // This is an EDNS0 (OPT Record) with no rdata // We can safely return here. break } edns := make([]EDNS0, 0) Option: code := uint16(0) if off+2 > lenmsg { return lenmsg, &Error{err: "overflow unpacking opt"} } code, off = unpackUint16(msg, off) optlen, off1 := unpackUint16(msg, off) if off1+int(optlen) > off+rdlength { return lenmsg, &Error{err: "overflow unpacking opt"} } switch code { case EDNS0NSID: e := new(EDNS0_NSID) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) case EDNS0SUBNET: e := new(EDNS0_SUBNET) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) case EDNS0UL: e := new(EDNS0_UL) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) case EDNS0LLQ: e := new(EDNS0_LLQ) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) case EDNS0DAU: e := new(EDNS0_DAU) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) case EDNS0DHU: e := new(EDNS0_DHU) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) case EDNS0N3U: e := new(EDNS0_N3U) e.unpack(msg[off1 : off1+int(optlen)]) edns = append(edns, e) off = off1 + int(optlen) default: // do nothing? off = off1 + int(optlen) } if off < endrr { goto Option } fv.Set(reflect.ValueOf(edns)) case `dns:"a"`: if off == lenmsg { break // dyn. update } if off+net.IPv4len > lenmsg { return lenmsg, &Error{err: "overflow unpacking a"} } fv.Set(reflect.ValueOf(net.IPv4(msg[off], msg[off+1], msg[off+2], msg[off+3]))) off += net.IPv4len case `dns:"aaaa"`: if off == lenmsg { break } if off+net.IPv6len > lenmsg { return lenmsg, &Error{err: "overflow unpacking aaaa"} } fv.Set(reflect.ValueOf(net.IP{msg[off], msg[off+1], msg[off+2], msg[off+3], msg[off+4], msg[off+5], msg[off+6], msg[off+7], msg[off+8], msg[off+9], msg[off+10], msg[off+11], msg[off+12], msg[off+13], msg[off+14], msg[off+15]})) off += net.IPv6len case `dns:"wks"`: // Rest of the record is the bitmap rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) endrr := rdstart + rdlength serv := make([]uint16, 0) j := 0 for off < endrr { b := msg[off] // Check the bits one by one, and set the type if b&0x80 == 0x80 { serv = append(serv, uint16(j*8+0)) } if b&0x40 == 0x40 { serv = append(serv, uint16(j*8+1)) } if b&0x20 == 0x20 { serv = append(serv, uint16(j*8+2)) } if b&0x10 == 0x10 { serv = append(serv, uint16(j*8+3)) } if b&0x8 == 0x8 { serv = append(serv, uint16(j*8+4)) } if b&0x4 == 0x4 { serv = append(serv, uint16(j*8+5)) } if b&0x2 == 0x2 { serv = append(serv, uint16(j*8+6)) } if b&0x1 == 0x1 { serv = append(serv, uint16(j*8+7)) } j++ off++ } fv.Set(reflect.ValueOf(serv)) case `dns:"nsec"`: // NSEC/NSEC3 if off == lenmsg { break } // Rest of the record is the type bitmap rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) endrr := rdstart + rdlength if off+2 > lenmsg { return lenmsg, &Error{err: "overflow unpacking nsecx"} } nsec := make([]uint16, 0) length := 0 window := 0 for off+2 < endrr { window = int(msg[off]) length = int(msg[off+1]) //println("off, windows, length, end", off, window, length, endrr) if length == 0 { // A length window of zero is strange. If there // the window should not have been specified. Bail out // println("dns: length == 0 when unpacking NSEC") return lenmsg, ErrRdata } if length > 32 { return lenmsg, ErrRdata } // Walk the bytes in the window - and check the bit settings... off += 2 for j := 0; j < length; j++ { b := msg[off+j] // Check the bits one by one, and set the type if b&0x80 == 0x80 { nsec = append(nsec, uint16(window*256+j*8+0)) } if b&0x40 == 0x40 { nsec = append(nsec, uint16(window*256+j*8+1)) } if b&0x20 == 0x20 { nsec = append(nsec, uint16(window*256+j*8+2)) } if b&0x10 == 0x10 { nsec = append(nsec, uint16(window*256+j*8+3)) } if b&0x8 == 0x8 { nsec = append(nsec, uint16(window*256+j*8+4)) } if b&0x4 == 0x4 { nsec = append(nsec, uint16(window*256+j*8+5)) } if b&0x2 == 0x2 { nsec = append(nsec, uint16(window*256+j*8+6)) } if b&0x1 == 0x1 { nsec = append(nsec, uint16(window*256+j*8+7)) } } off += length } fv.Set(reflect.ValueOf(nsec)) } case reflect.Struct: off, err = unpackStructValue(fv, msg, off) if err != nil { return lenmsg, err } if val.Type().Field(i).Name == "Hdr" { rdstart = off } case reflect.Uint8: if off == lenmsg { break } if off+1 > lenmsg { return lenmsg, &Error{err: "overflow unpacking uint8"} } fv.SetUint(uint64(uint8(msg[off]))) off++ case reflect.Uint16: if off == lenmsg { break } var i uint16 if off+2 > lenmsg { return lenmsg, &Error{err: "overflow unpacking uint16"} } i, off = unpackUint16(msg, off) fv.SetUint(uint64(i)) case reflect.Uint32: if off == lenmsg { break } if off+4 > lenmsg { return lenmsg, &Error{err: "overflow unpacking uint32"} } fv.SetUint(uint64(uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]))) off += 4 case reflect.Uint64: switch val.Type().Field(i).Tag { default: if off+8 > lenmsg { return lenmsg, &Error{err: "overflow unpacking uint64"} } fv.SetUint(uint64(uint64(msg[off])<<56 | uint64(msg[off+1])<<48 | uint64(msg[off+2])<<40 | uint64(msg[off+3])<<32 | uint64(msg[off+4])<<24 | uint64(msg[off+5])<<16 | uint64(msg[off+6])<<8 | uint64(msg[off+7]))) off += 8 case `dns:"uint48"`: // Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes) if off+6 > lenmsg { return lenmsg, &Error{err: "overflow unpacking uint64 as uint48"} } fv.SetUint(uint64(uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 | uint64(msg[off+4])<<8 | uint64(msg[off+5]))) off += 6 } case reflect.String: var s string if off == lenmsg { break } switch val.Type().Field(i).Tag { default: return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")} case `dns:"hex"`: // Rest of the RR is hex encoded, network order an issue here? rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) endrr := rdstart + rdlength if endrr > lenmsg { return lenmsg, &Error{err: "overflow unpacking hex"} } s = hex.EncodeToString(msg[off:endrr]) off = endrr case `dns:"base64"`: // Rest of the RR is base64 encoded value rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) endrr := rdstart + rdlength if endrr > lenmsg { return lenmsg, &Error{err: "overflow unpacking base64"} } s = unpackBase64(msg[off:endrr]) off = endrr case `dns:"cdomain-name"`: fallthrough case `dns:"domain-name"`: if off == lenmsg { // zero rdata foo, OK for dyn. updates break } s, off, err = UnpackDomainName(msg, off) if err != nil { return lenmsg, err } case `dns:"size-base32"`: var size int switch val.Type().Name() { case "NSEC3": switch val.Type().Field(i).Name { case "NextDomain": name := val.FieldByName("HashLength") size = int(name.Uint()) } } if off+size > lenmsg { return lenmsg, &Error{err: "overflow unpacking base32"} } s = unpackBase32(msg[off : off+size]) off += size case `dns:"size-hex"`: // a "size" string, but it must be encoded in hex in the string var size int switch val.Type().Name() { case "NSEC3": switch val.Type().Field(i).Name { case "Salt": name := val.FieldByName("SaltLength") size = int(name.Uint()) case "NextDomain": name := val.FieldByName("HashLength") size = int(name.Uint()) } case "TSIG": switch val.Type().Field(i).Name { case "MAC": name := val.FieldByName("MACSize") size = int(name.Uint()) case "OtherData": name := val.FieldByName("OtherLen") size = int(name.Uint()) } } if off+size > lenmsg { return lenmsg, &Error{err: "overflow unpacking hex"} } s = hex.EncodeToString(msg[off : off+size]) off += size case `dns:"txt"`: // 1 txt piece rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) Txt: if off >= lenmsg || off+1+int(msg[off]) > lenmsg { return lenmsg, &Error{err: "overflow unpacking txt"} } n := int(msg[off]) off++ for i := 0; i < n; i++ { s += string(msg[off+i]) } off += n if off < rdlength { // More to goto Txt } case "": if off >= lenmsg || off+1+int(msg[off]) > lenmsg { return lenmsg, &Error{err: "overflow unpacking string"} } n := int(msg[off]) off++ for i := 0; i < n; i++ { s += string(msg[off+i]) } off += n } fv.SetString(s) } } return off, nil } // Helper function for unpacking func unpackUint16(msg []byte, off int) (v uint16, off1 int) { v = uint16(msg[off])<<8 | uint16(msg[off+1]) off1 = off + 2 return } // UnpackStruct unpacks a binary message from offset off to the interface // value given. func UnpackStruct(any interface{}, msg []byte, off int) (off1 int, err error) { off, err = unpackStructValue(structValue(any), msg, off) return off, err } func unpackBase32(b []byte) string { b32 := make([]byte, base32.HexEncoding.EncodedLen(len(b))) base32.HexEncoding.Encode(b32, b) return string(b32) } func unpackBase64(b []byte) string { b64 := make([]byte, base64.StdEncoding.EncodedLen(len(b))) base64.StdEncoding.Encode(b64, b) return string(b64) } // Helper function for packing func packUint16(i uint16) (byte, byte) { return byte(i >> 8), byte(i) } func packBase64(s []byte) ([]byte, error) { b64len := base64.StdEncoding.DecodedLen(len(s)) buf := make([]byte, b64len) n, err := base64.StdEncoding.Decode(buf, []byte(s)) if err != nil { return nil, err } buf = buf[:n] return buf, nil } // Helper function for packing, mostly used in dnssec.go func packBase32(s []byte) ([]byte, error) { b32len := base32.HexEncoding.DecodedLen(len(s)) buf := make([]byte, b32len) n, err := base32.HexEncoding.Decode(buf, []byte(s)) if err != nil { return nil, err } buf = buf[:n] return buf, nil } // Resource record packer, pack rr into msg[off:]. See PackDomainName for documentation // about the compression. func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { if rr == nil { return len(msg), &Error{err: "nil rr"} } off1, err = packStructCompress(rr, msg, off, compression, compress) if err != nil { return len(msg), err } rawSetRdlength(msg, off, off1) return off1, nil } // Resource record unpacker, unpack msg[off:] into an RR. func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { // unpack just the header, to find the rr type and length var h RR_Header off0 := off if off, err = UnpackStruct(&h, msg, off); err != nil { return nil, len(msg), err } end := off + int(h.Rdlength) // make an rr of that type and re-unpack. mk, known := rr_mk[h.Rrtype] if !known { rr = new(RFC3597) } else { rr = mk() } off, err = UnpackStruct(rr, msg, off0) if off != end { return &h, end, &Error{err: "bad rdlength"} } return rr, off, err } // Reverse a map func reverseInt8(m map[uint8]string) map[string]uint8 { n := make(map[string]uint8) for u, s := range m { n[s] = u } return n } func reverseInt16(m map[uint16]string) map[string]uint16 { n := make(map[string]uint16) for u, s := range m { n[s] = u } return n } func reverseInt(m map[int]string) map[string]int { n := make(map[string]int) for u, s := range m { n[s] = u } return n } // Convert a MsgHdr to a string, with dig-like headers: // //;; opcode: QUERY, status: NOERROR, id: 48404 // //;; flags: qr aa rd ra; func (h *MsgHdr) String() string { if h == nil { return " MsgHdr" } s := ";; opcode: " + OpcodeToString[h.Opcode] s += ", status: " + RcodeToString[h.Rcode] s += ", id: " + strconv.Itoa(int(h.Id)) + "\n" s += ";; flags:" if h.Response { s += " qr" } if h.Authoritative { s += " aa" } if h.Truncated { s += " tc" } if h.RecursionDesired { s += " rd" } if h.RecursionAvailable { s += " ra" } if h.Zero { // Hmm s += " z" } if h.AuthenticatedData { s += " ad" } if h.CheckingDisabled { s += " cd" } s += ";" return s } // Pack packs a Msg: it is converted to to wire format. // If the dns.Compress is true the message will be in compressed wire format. func (dns *Msg) Pack() (msg []byte, err error) { var dh Header var compression map[string]int if dns.Compress { compression = make(map[string]int) // Compression pointer mappings } else { compression = nil } // Convert convenient Msg into wire-like Header. dh.Id = dns.Id dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode) if dns.Response { dh.Bits |= _QR } if dns.Authoritative { dh.Bits |= _AA } if dns.Truncated { dh.Bits |= _TC } if dns.RecursionDesired { dh.Bits |= _RD } if dns.RecursionAvailable { dh.Bits |= _RA } if dns.Zero { dh.Bits |= _Z } if dns.AuthenticatedData { dh.Bits |= _AD } if dns.CheckingDisabled { dh.Bits |= _CD } // Prepare variable sized arrays. question := dns.Question answer := dns.Answer ns := dns.Ns extra := dns.Extra dh.Qdcount = uint16(len(question)) dh.Ancount = uint16(len(answer)) dh.Nscount = uint16(len(ns)) dh.Arcount = uint16(len(extra)) msg = make([]byte, dns.packLen()+1) // Pack it in: header and then the pieces. off := 0 off, err = packStructCompress(&dh, msg, off, compression, dns.Compress) if err != nil { return nil, err } for i := 0; i < len(question); i++ { off, err = packStructCompress(&question[i], msg, off, compression, dns.Compress) if err != nil { return nil, err } } for i := 0; i < len(answer); i++ { off, err = PackRR(answer[i], msg, off, compression, dns.Compress) if err != nil { return nil, err } } for i := 0; i < len(ns); i++ { off, err = PackRR(ns[i], msg, off, compression, dns.Compress) if err != nil { return nil, err } } for i := 0; i < len(extra); i++ { off, err = PackRR(extra[i], msg, off, compression, dns.Compress) if err != nil { return nil, err } } return msg[:off], nil } // Unpack unpacks a binary message to a Msg structure. func (dns *Msg) Unpack(msg []byte) (err error) { // Header. var dh Header off := 0 if off, err = UnpackStruct(&dh, msg, off); err != nil { return err } dns.Id = dh.Id dns.Response = (dh.Bits & _QR) != 0 dns.Opcode = int(dh.Bits>>11) & 0xF dns.Authoritative = (dh.Bits & _AA) != 0 dns.Truncated = (dh.Bits & _TC) != 0 dns.RecursionDesired = (dh.Bits & _RD) != 0 dns.RecursionAvailable = (dh.Bits & _RA) != 0 dns.Zero = (dh.Bits & _Z) != 0 dns.AuthenticatedData = (dh.Bits & _AD) != 0 dns.CheckingDisabled = (dh.Bits & _CD) != 0 dns.Rcode = int(dh.Bits & 0xF) // Arrays. dns.Question = make([]Question, dh.Qdcount) dns.Answer = make([]RR, dh.Ancount) dns.Ns = make([]RR, dh.Nscount) dns.Extra = make([]RR, dh.Arcount) for i := 0; i < len(dns.Question); i++ { off, err = UnpackStruct(&dns.Question[i], msg, off) if err != nil { return err } } for i := 0; i < len(dns.Answer); i++ { dns.Answer[i], off, err = UnpackRR(msg, off) if err != nil { return err } } for i := 0; i < len(dns.Ns); i++ { dns.Ns[i], off, err = UnpackRR(msg, off) if err != nil { return err } } for i := 0; i < len(dns.Extra); i++ { dns.Extra[i], off, err = UnpackRR(msg, off) if err != nil { return err } } if off != len(msg) { // TODO(miek) make this an error? // use PackOpt to let people tell how detailed the error reporting should be? // println("dns: extra bytes in dns packet", off, "<", len(msg)) } return nil } // Convert a complete message to a string with dig-like output. func (dns *Msg) String() string { if dns == nil { return " MsgHdr" } s := dns.MsgHdr.String() + " " s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" if len(dns.Question) > 0 { s += "\n;; QUESTION SECTION:\n" for i := 0; i < len(dns.Question); i++ { s += dns.Question[i].String() + "\n" } } if len(dns.Answer) > 0 { s += "\n;; ANSWER SECTION:\n" for i := 0; i < len(dns.Answer); i++ { if dns.Answer[i] != nil { s += dns.Answer[i].String() + "\n" } } } if len(dns.Ns) > 0 { s += "\n;; AUTHORITY SECTION:\n" for i := 0; i < len(dns.Ns); i++ { if dns.Ns[i] != nil { s += dns.Ns[i].String() + "\n" } } } if len(dns.Extra) > 0 { s += "\n;; ADDITIONAL SECTION:\n" for i := 0; i < len(dns.Extra); i++ { if dns.Extra[i] != nil { s += dns.Extra[i].String() + "\n" } } } return s } // packLen returns the message length when in UNcompressed wire format. func (dns *Msg) packLen() int { // Message header is always 12 bytes l := 12 for i := 0; i < len(dns.Question); i++ { l += dns.Question[i].len() } for i := 0; i < len(dns.Answer); i++ { l += dns.Answer[i].len() } for i := 0; i < len(dns.Ns); i++ { l += dns.Ns[i].len() } for i := 0; i < len(dns.Extra); i++ { l += dns.Extra[i].len() } return l } // Len returns the message length when in (un)compressed wire format. // If dns.Compress is true compression it is taken into account. Len() // is provided to be a faster way to get the size of the resulting packet, // than packing it, measuring the size and discarding the buffer. func (dns *Msg) Len() int { // Message header is always 12 bytes l := 12 var compression map[string]int if dns.Compress { compression = make(map[string]int) } for i := 0; i < len(dns.Question); i++ { l += dns.Question[i].len() if dns.Compress { compressionLenHelper(compression, dns.Question[i].Name) } } for i := 0; i < len(dns.Answer); i++ { l += dns.Answer[i].len() if dns.Compress { k, ok := compressionLenSearch(compression, dns.Answer[i].Header().Name) if ok { l += 1 - k } else { compressionLenHelper(compression, dns.Answer[i].Header().Name) } l += 1 - compressionLenType(compression, dns.Answer[i]) } } for i := 0; i < len(dns.Ns); i++ { l += dns.Ns[i].len() if dns.Compress { k, ok := compressionLenSearch(compression, dns.Ns[i].Header().Name) if ok { l += 1 - k } else { compressionLenHelper(compression, dns.Ns[i].Header().Name) } l += 1 - compressionLenType(compression, dns.Ns[i]) } } for i := 0; i < len(dns.Extra); i++ { if dns.Compress { k, ok := compressionLenSearch(compression, dns.Extra[i].Header().Name) if ok { l += 1 - k } else { compressionLenHelper(compression, dns.Extra[i].Header().Name) } l += 1 - compressionLenType(compression, dns.Extra[i]) } } return l } func (dns *Msg) copy() *Msg { r1 := new(Msg) r1.MsgHdr = dns.MsgHdr r1.Compress = dns.Compress r1.Question = make([]Question, len(dns.Question)) r1.Answer = make([]RR, len(dns.Answer)) r1.Ns = make([]RR, len(dns.Ns)) r1.Extra = make([]RR, len(dns.Extra)) copy(r1.Question, dns.Question) copy(r1.Answer, dns.Answer) copy(r1.Ns, dns.Ns) copy(r1.Extra, dns.Extra) return r1 } // Put the parts of the name in the compression map. func compressionLenHelper(c map[string]int, s string) { pref := "" lbs := SplitDomainName(s) for j := len(lbs) - 1; j >= 0; j-- { c[lbs[j]+"."+pref] = 1 + len(pref) + len(lbs[j]) pref = lbs[j] + "." + pref } } // Look for each part in the compression map and returns its length func compressionLenSearch(c map[string]int, s string) (int, bool) { off := 0 end := false for { if end { break } if _, ok := c[s[off:]]; ok { return len(s[off:]), true } off, end = NextLabel(s, off) } // TODO(miek): not sure if need, leave this for later debugging if _, ok := c[s[off:]]; ok { return len(s[off:]), true } return 0, false } // Check the ownernames too of the types that have cdomain, do // this manually to avoid reflection. func compressionLenType(c map[string]int, r RR) int { switch x := r.(type) { case *NS: k, ok := compressionLenSearch(c, x.Ns) if ok { return k } else { compressionLenHelper(c, x.Ns) } case *MX: k, ok := compressionLenSearch(c, x.Mx) if ok { return k } else { compressionLenHelper(c, x.Mx) } case *CNAME: k, ok := compressionLenSearch(c, x.Target) if ok { return k } else { compressionLenHelper(c, x.Target) } case *PTR: k, ok := compressionLenSearch(c, x.Ptr) if ok { return k } else { compressionLenHelper(c, x.Ptr) } case *SOA: k, ok := compressionLenSearch(c, x.Ns) if ok { return k } else { compressionLenHelper(c, x.Ns) } k, ok = compressionLenSearch(c, x.Mbox) if ok { return k } else { compressionLenHelper(c, x.Mbox) } case *MB: k, ok := compressionLenSearch(c, x.Mb) if ok { return k } else { compressionLenHelper(c, x.Mb) } case *MG: k, ok := compressionLenSearch(c, x.Mg) if ok { return k } else { compressionLenHelper(c, x.Mg) } case *MR: k, ok := compressionLenSearch(c, x.Mr) if ok { return k } else { compressionLenHelper(c, x.Mr) } case *MF: k, ok := compressionLenSearch(c, x.Mf) if ok { return k } else { compressionLenHelper(c, x.Mf) } case *MD: k, ok := compressionLenSearch(c, x.Md) if ok { return k } else { compressionLenHelper(c, x.Md) } case *RT: k, ok := compressionLenSearch(c, x.Host) if ok { return k } else { compressionLenHelper(c, x.Host) } case *MINFO: k, ok := compressionLenSearch(c, x.Rmail) if ok { return k } else { compressionLenHelper(c, x.Rmail) } k, ok = compressionLenSearch(c, x.Email) if ok { return k } else { compressionLenHelper(c, x.Email) } case *AFSDB: k, ok := compressionLenSearch(c, x.Hostname) if ok { return k } else { compressionLenHelper(c, x.Hostname) } } return 1 // noop when nothing is found } // Id return a 16 bits random number to be used as a // message id. The random provided should be good enough. func Id() uint16 { return uint16(rand.Int()) ^ uint16(time.Now().Nanosecond()) } golang-dns-0.0~git20130912/nsecx.go000066400000000000000000000021561221430110200165630ustar00rootroot00000000000000// 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. package dns import ( "crypto/sha1" "hash" "io" "strings" ) type saltWireFmt struct { Salt string `dns:"size-hex"` } // HashName hashes a string (label) according to RFC 5155. It returns the hashed string. func HashName(label string, ha uint8, iter uint16, salt string) string { saltwire := new(saltWireFmt) saltwire.Salt = salt wire := make([]byte, DefaultMsgSize) n, err := PackStruct(saltwire, wire, 0) if err != nil { return "" } wire = wire[:n] name := make([]byte, 255) off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false) if err != nil { return "" } name = name[:off] var s hash.Hash switch ha { case SHA1: s = sha1.New() default: return "" } // k = 0 name = append(name, wire...) io.WriteString(s, string(name)) nsec3 := s.Sum(nil) // k > 0 for k := uint16(0); k < iter; k++ { s.Reset() nsec3 = append(nsec3, wire...) io.WriteString(s, string(nsec3)) nsec3 = s.Sum(nil) } return unpackBase32(nsec3) } golang-dns-0.0~git20130912/nsecx_test.go000066400000000000000000000010011221430110200176060ustar00rootroot00000000000000// 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. package dns import ( "testing" ) func TestPackNsec3(t *testing.T) { nsec3 := HashName("dnsex.nl.", SHA1, 0, "DEAD") if nsec3 != "ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J" { t.Logf("%v\n", nsec3) t.Fail() } nsec3 = HashName("a.b.c.example.org.", SHA1, 2, "DEAD") if nsec3 != "6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG" { t.Logf("%v\n", nsec3) t.Fail() } } golang-dns-0.0~git20130912/parse_test.go000066400000000000000000000622701221430110200176170ustar00rootroot00000000000000// 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. package dns import ( "fmt" "net" "os" "strconv" "strings" "testing" "time" ) func TestDotInName(t *testing.T) { buf := make([]byte, 20) PackDomainName("aa\\.bb.nl.", buf, 0, nil, false) // index 3 must be a real dot if buf[3] != '.' { t.Log("Dot should be a real dot") t.Fail() } if buf[6] != 2 { t.Log("This must have the value 2") t.Fail() } dom, _, _ := UnpackDomainName(buf, 0) // printing it should yield the backspace again if dom != "aa\\.bb.nl." { t.Log("Dot should have been escaped: " + dom) t.Fail() } } func TestTooLongDomainName(t *testing.T) { l := "aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt." dom := l + l + l + l + l + l + l _, e := NewRR(dom + " IN A 127.0.0.1") if e == nil { t.Log("Should be too long") t.Fail() } else { t.Logf("Error is %s", e.Error()) } _, e = NewRR("..com. IN A 127.0.0.1") if e == nil { t.Log("Should fail") t.Fail() } else { t.Logf("Error is %s", e.Error()) } } func TestDomainName(t *testing.T) { tests := []string{"r\\.gieben.miek.nl.", "www\\.www.miek.nl.", "www.*.miek.nl.", "www.*.miek.nl.", } dbuff := make([]byte, 40) for _, ts := range tests { if _, err := PackDomainName(ts, dbuff, 0, nil, false); err != nil { t.Log("Not a valid domain name") t.Fail() continue } n, _, err := UnpackDomainName(dbuff, 0) if err != nil { t.Log("Failed to unpack packed domain name") t.Fail() continue } if ts != n { t.Logf("Must be equal: in: %s, out: %s\n", ts, n) t.Fail() } } } func TestParseDirectiveMisc(t *testing.T) { tests := map[string]string{ "$ORIGIN miek.nl.\na IN NS b": "a.miek.nl.\t3600\tIN\tNS\tb.miek.nl.", "$TTL 2H\nmiek.nl. IN NS b.": "miek.nl.\t7200\tIN\tNS\tb.", "miek.nl. 1D IN NS b.": "miek.nl.\t86400\tIN\tNS\tb.", `name. IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 5m ; refresh (5 minutes) 5m ; retry (5 minutes) 2w ; expire (2 weeks) 300 ; minimum (5 minutes) )`: "name.\t3600\tIN\tSOA\ta6.nstld.com. hostmaster.nic.name. 203362132 300 300 1209600 300", ". 3600000 IN NS ONE.MY-ROOTS.NET.": ".\t3600000\tIN\tNS\tONE.MY-ROOTS.NET.", "ONE.MY-ROOTS.NET. 3600000 IN A 192.168.1.1": "ONE.MY-ROOTS.NET.\t3600000\tIN\tA\t192.168.1.1", } for i, o := range tests { rr, e := NewRR(i) if e != nil { t.Log("Failed to parse RR: " + e.Error()) t.Fail() continue } if rr.String() != o { t.Logf("`%s' should be equal to\n`%s', but is `%s'\n", i, o, rr.String()) t.Fail() } else { t.Logf("RR is OK: `%s'", rr.String()) } } } func TestParseNSEC(t *testing.T) { nsectests := map[string]string{ "nl. IN NSEC3PARAM 1 0 5 30923C44C6CBBB8F": "nl.\t3600\tIN\tNSEC3PARAM\t1 0 5 30923C44C6CBBB8F", "p2209hipbpnm681knjnu0m1febshlv4e.nl. IN NSEC3 1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM": "p2209hipbpnm681knjnu0m1febshlv4e.nl.\t3600\tIN\tNSEC3\t1 1 5 30923C44C6CBBB8F P90DG1KE8QEAN0B01613LHQDG0SOJ0TA NS SOA TXT RRSIG DNSKEY NSEC3PARAM", "localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC", "localhost.dnssex.nl. IN NSEC www.dnssex.nl. A RRSIG NSEC TYPE65534": "localhost.dnssex.nl.\t3600\tIN\tNSEC\twww.dnssex.nl. A RRSIG NSEC TYPE65534", } for i, o := range nsectests { rr, e := NewRR(i) if e != nil { t.Log("Failed to parse RR: " + e.Error()) t.Fail() continue } if rr.String() != o { t.Logf("`%s' should be equal to\n`%s', but is `%s'\n", i, o, rr.String()) t.Fail() } else { t.Logf("RR is OK: `%s'", rr.String()) } } } func TestParseLOC(t *testing.T) { lt := map[string]string{ "SW1A2AA.find.me.uk. LOC 51 30 12.748 N 00 07 39.611 W 0.00m 0.00m 0.00m 0.00m": "SW1A2AA.find.me.uk.\t3600\tIN\tLOC\t51 30 12.748 N 00 07 39.611 W 0.00m 0.00m 0.00m 0.00m", "SW1A2AA.find.me.uk. LOC 51 0 0.0 N 00 07 39.611 W 0.00m 0.00m 0.00m 0.00m": "SW1A2AA.find.me.uk.\t3600\tIN\tLOC\t51 00 0.000 N 00 07 39.611 W 0.00m 0.00m 0.00m 0.00m", } for i, o := range lt { rr, e := NewRR(i) if e != nil { t.Log("Failed to parse RR: " + e.Error()) t.Fail() continue } if rr.String() != o { t.Logf("`%s' should be equal to\n`%s', but is `%s'\n", i, o, rr.String()) t.Fail() } else { t.Logf("RR is OK: `%s'", rr.String()) } } } func TestParseDS(t *testing.T) { dt := map[string]string{ "example.net. 3600 IN DS 40692 12 3 22261A8B0E0D799183E35E24E2AD6BB58533CBA7E3B14D659E9CA09B 2071398F": "example.net.\t3600\tIN\tDS\t40692 12 3 22261A8B0E0D799183E35E24E2AD6BB58533CBA7E3B14D659E9CA09B2071398F", } for i, o := range dt { rr, e := NewRR(i) if e != nil { t.Log("Failed to parse RR: " + e.Error()) t.Fail() continue } if rr.String() != o { t.Logf("`%s' should be equal to\n`%s', but is `%s'\n", i, o, rr.String()) t.Fail() } else { t.Logf("RR is OK: `%s'", rr.String()) } } } func TestQuotes(t *testing.T) { tests := map[string]string{ `t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"", `t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\n bc\"", `t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"", `t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"", `t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", `t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"", `t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"", `t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa \"", `t.example.com. IN TXT aaa aaa;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa aaa\"", `t.example.com. IN TXT aaa aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa aaa\"", `t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", "cid.urn.arpa. NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.", "cid.urn.arpa. NAPTR 100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.", "cid.urn.arpa. NAPTR 100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.gatech.edu.", "cid.urn.arpa. NAPTR 100 10 \"\" \"\" \"/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\" .": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 10 \"\" \"\" \"/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\" .", } for i, o := range tests { rr, e := NewRR(i) if e != nil { t.Log("Failed to parse RR: " + e.Error()) t.Fail() continue } if rr.String() != o { t.Logf("`%s' should be equal to\n`%s', but is\n`%s'\n", i, o, rr.String()) t.Fail() } else { t.Logf("RR is OK: `%s'", rr.String()) } } } func TestParseBrace(t *testing.T) { tests := map[string]string{ "(miek.nl.) 3600 IN A 127.0.1.1": "miek.nl.\t3600\tIN\tA\t127.0.1.1", "miek.nl. (3600) IN MX (10) elektron.atoom.net.": "miek.nl.\t3600\tIN\tMX\t10 elektron.atoom.net.", `miek.nl. IN ( 3600 A 127.0.0.1)`: "miek.nl.\t3600\tIN\tA\t127.0.0.1", "(miek.nl.) (A) (127.0.2.1)": "miek.nl.\t3600\tIN\tA\t127.0.2.1", "miek.nl A 127.0.3.1": "miek.nl.\t3600\tIN\tA\t127.0.3.1", "_ssh._tcp.local. 60 IN (PTR) stora._ssh._tcp.local.": "_ssh._tcp.local.\t60\tIN\tPTR\tstora._ssh._tcp.local.", "miek.nl. NS ns.miek.nl": "miek.nl.\t3600\tIN\tNS\tns.miek.nl.", `(miek.nl.) ( (IN) (AAAA) (::1) )`: "miek.nl.\t3600\tIN\tAAAA\t::1", `(miek.nl.) ( (IN) (AAAA) (::1))`: "miek.nl.\t3600\tIN\tAAAA\t::1", "miek.nl. IN AAAA ::2": "miek.nl.\t3600\tIN\tAAAA\t::2", `((m)(i)ek.(n)l.) (SOA) (soa.) (soa.) ( 2009032802 ; serial 21600 ; refresh (6 hours) 7(2)00 ; retry (2 hours) 604()800 ; expire (1 week) 3600 ; minimum (1 hour) )`: "miek.nl.\t3600\tIN\tSOA\tsoa. soa. 2009032802 21600 7200 604800 3600", "miek\\.nl. IN A 127.0.0.10": "miek\\.nl.\t3600\tIN\tA\t127.0.0.10", "miek.nl. IN A 127.0.0.11": "miek.nl.\t3600\tIN\tA\t127.0.0.11", "miek.nl. A 127.0.0.12": "miek.nl.\t3600\tIN\tA\t127.0.0.12", `miek.nl. 86400 IN SOA elektron.atoom.net. miekg.atoom.net. ( 2009032802 ; serial 21600 ; refresh (6 hours) 7200 ; retry (2 hours) 604800 ; expire (1 week) 3600 ; minimum (1 hour) )`: "miek.nl.\t86400\tIN\tSOA\telektron.atoom.net. miekg.atoom.net. 2009032802 21600 7200 604800 3600", } for i, o := range tests { rr, e := NewRR(i) if e != nil { t.Log("Failed to parse RR: " + e.Error() + "\n\t" + i) t.Fail() continue } if rr.String() != o { t.Logf("`%s' should be equal to\n`%s', but is `%s'\n", i, o, rr.String()) t.Fail() } else { t.Logf("RR is OK: `%s'", rr.String()) } } } func TestParseFailure(t *testing.T) { tests := []string{"miek.nl. IN A 327.0.0.1", "miek.nl. IN AAAA ::x", "miek.nl. IN MX a0 miek.nl.", "miek.nl aap IN MX mx.miek.nl.", // "miek.nl. IN CNAME ", // actually valid nowadays, zero size rdata "miek.nl. IN CNAME ..", "miek.nl. PA MX 10 miek.nl.", "miek.nl. ) IN MX 10 miek.nl.", } for _, s := range tests { _, err := NewRR(s) if err == nil { t.Logf("Should have triggered an error: \"%s\"", s) t.Fail() } } } /* // A bit useless, how to use b.N?. It always returns 0 func BenchmarkZoneParsing(b *testing.B) { b.StopTimer() f, err := os.Open("t/miek.nl.signed_test") if err != nil { return } defer f.Close() b.StartTimer() for i := 0; i < b.N; i++ { to := ParseZone(f, "", "t/miek.nl.signed_test") for _ = range to { } } } */ func TestZoneParsing(t *testing.T) { f, err := os.Open("t/miek.nl.signed_test") if err != nil { return } defer f.Close() start := time.Now().UnixNano() to := ParseZone(f, "", "t/miek.nl.signed_test") var i int for x := range to { t.Logf("%s\n", x.RR) i++ } delta := time.Now().UnixNano() - start t.Logf("%d RRs parsed in %.2f s (%.2f RR/s)", i, float32(delta)/1e9, float32(i)/(float32(delta)/1e9)) } func ExampleZone() { zone := `$ORIGIN . $TTL 3600 ; 1 hour name IN SOA a6.nstld.com. hostmaster.nic.name. ( 203362132 ; serial 300 ; refresh (5 minutes) 300 ; retry (5 minutes) 1209600 ; expire (2 weeks) 300 ; minimum (5 minutes) ) $TTL 10800 ; 3 hours name. 10800 IN NS name. IN NS g6.nstld.com. 7200 NS h6.nstld.com. 3600 IN NS j6.nstld.com. IN 3600 NS k6.nstld.com. NS l6.nstld.com. NS a6.nstld.com. NS c6.nstld.com. NS d6.nstld.com. NS f6.nstld.com. NS m6.nstld.com. ( NS m7.nstld.com. ) $ORIGIN name. 0-0onlus NS ns7.ehiweb.it. NS ns8.ehiweb.it. 0-g MX 10 mx01.nic MX 10 mx02.nic MX 10 mx03.nic MX 10 mx04.nic $ORIGIN 0-g.name moutamassey NS ns01.yahoodomains.jp. NS ns02.yahoodomains.jp. ` to := ParseZone(strings.NewReader(zone), "", "testzone") for x := range to { fmt.Printf("%s\n", x.RR) } // Output: // name. 3600 IN SOA a6.nstld.com. hostmaster.nic.name. 203362132 300 300 1209600 300 // name. 10800 IN NS name. // name. 10800 IN NS g6.nstld.com. // name. 7200 IN NS h6.nstld.com. // name. 3600 IN NS j6.nstld.com. // name. 3600 IN NS k6.nstld.com. // name. 10800 IN NS l6.nstld.com. // name. 10800 IN NS a6.nstld.com. // name. 10800 IN NS c6.nstld.com. // name. 10800 IN NS d6.nstld.com. // name. 10800 IN NS f6.nstld.com. // name. 10800 IN NS m6.nstld.com. // name. 10800 IN NS m7.nstld.com. // 0-0onlus.name. 10800 IN NS ns7.ehiweb.it. // 0-0onlus.name. 10800 IN NS ns8.ehiweb.it. // 0-g.name. 10800 IN MX 10 mx01.nic.name. // 0-g.name. 10800 IN MX 10 mx02.nic.name. // 0-g.name. 10800 IN MX 10 mx03.nic.name. // 0-g.name. 10800 IN MX 10 mx04.nic.name. // moutamassey.0-g.name.name. 10800 IN NS ns01.yahoodomains.jp. // moutamassey.0-g.name.name. 10800 IN NS ns02.yahoodomains.jp. } func ExampleHIP() { h := `www.example.com IN HIP ( 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p 9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQ b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. )` if hip, err := NewRR(h); err == nil { fmt.Printf("%s\n", hip.String()) } // Output: // www.example.com. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. } func ExampleSOA() { s := "example.com. 1000 SOA master.example.com. admin.example.com. 1 4294967294 4294967293 4294967295 100" if soa, err := NewRR(s); err == nil { fmt.Printf("%s\n", soa.String()) } // Output: // example.com. 1000 IN SOA master.example.com. admin.example.com. 1 4294967294 4294967293 4294967295 100 } func TestLineNumberError(t *testing.T) { s := "example.com. 1000 SOA master.example.com. admin.example.com. monkey 4294967294 4294967293 4294967295 100" if _, err := NewRR(s); err != nil { if err.Error() != "dns: bad SOA zone parameter: \"monkey\" at line: 1:68" { t.Logf("Not expecting this error: " + err.Error()) t.Fail() } } } // Test with no known RR on the line func TestLineNumberError2(t *testing.T) { tests := map[string]string{ "example.com. 1000 SO master.example.com. admin.example.com. 1 4294967294 4294967293 4294967295 100": "dns: expecting RR type or class, not this...: \"SO\" at line: 1:21", "example.com 1000 IN TALINK a.example.com. b..example.com.": "dns: bad TALINK NextName: \"b..example.com.\" at line: 1:57", "example.com 1000 IN TALINK ( a.example.com. b..example.com. )": "dns: bad TALINK NextName: \"b..example.com.\" at line: 1:60", `example.com 1000 IN TALINK ( a.example.com. bb..example.com. )`: "dns: bad TALINK NextName: \"bb..example.com.\" at line: 2:18", // This is a bug, it should report an error on line 1, but the new is already processed. `example.com 1000 IN TALINK ( a.example.com. b...example.com. )`: "dns: bad TALINK NextName: \"b...example.com.\" at line: 2:1"} for in, err := range tests { _, e := NewRR(in) if e == nil { t.Fail() } else { if e.Error() != err { t.Logf("%s\n", in) t.Logf("Error should be %s is %s\n", err, e.Error()) t.Fail() } } } } // Test if the calculations are correct func TestRfc1982(t *testing.T) { // If the current time and the timestamp are more than 68 years apart // it means the date has wrapped. 0 is 1970 // fall in the current 68 year span strtests := []string{"20120525134203", "19700101000000", "20380119031408"} for _, v := range strtests { if x, _ := StringToTime(v); v != TimeToString(x) { t.Logf("1982 arithmetic string failure %s (%s:%d)", v, TimeToString(x), x) t.Fail() } } inttests := map[uint32]string{0: "19700101000000", 1 << 31: "20380119031408", 1<<32 - 1: "21060207062815", } for i, v := range inttests { if TimeToString(i) != v { t.Logf("1982 arithmetic int failure %d:%s (%s)", i, v, TimeToString(i)) t.Fail() } } // Future tests, these dates get parsed to a date within the current 136 year span future := map[string]string{"22680119031408": "20631123173144", "19010101121212": "20370206184028", "19210101121212": "20570206184028", "19500101121212": "20860206184028", "19700101000000": "19700101000000", "19690101000000": "21050207062816", "29210101121212": "21040522212236", } for from, to := range future { x, _ := StringToTime(from) y := TimeToString(x) if y != to { t.Logf("1982 arithmetic future failure %s:%s (%s)", from, to, y) t.Fail() } } } func TestEmpty(t *testing.T) { for _ = range ParseZone(strings.NewReader(""), "", "") { t.Logf("Should be empty") t.Fail() } } func ExampleGenerate() { // From the manual: http://www.bind9.net/manual/bind/9.3.2/Bv9ARM.ch06.html#id2566761 zone := "$GENERATE 1-2 0 NS SERVER$.EXAMPLE.\n$GENERATE 1-8 $ CNAME $.0" to := ParseZone(strings.NewReader(zone), "0.0.192.IN-ADDR.ARPA.", "") for x := range to { if x.Error == nil { fmt.Printf("%s\n", x.RR.String()) } } // Output: // 0.0.0.192.IN-ADDR.ARPA. 3600 IN NS SERVER1.EXAMPLE. // 0.0.0.192.IN-ADDR.ARPA. 3600 IN NS SERVER2.EXAMPLE. // 1.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 1.0.0.0.192.IN-ADDR.ARPA. // 2.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 2.0.0.0.192.IN-ADDR.ARPA. // 3.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 3.0.0.0.192.IN-ADDR.ARPA. // 4.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 4.0.0.0.192.IN-ADDR.ARPA. // 5.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 5.0.0.0.192.IN-ADDR.ARPA. // 6.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 6.0.0.0.192.IN-ADDR.ARPA. // 7.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 7.0.0.0.192.IN-ADDR.ARPA. // 8.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 8.0.0.0.192.IN-ADDR.ARPA. } func TestSRVPacking(t *testing.T) { msg := Msg{} things := []string{"1.2.3.4:8484", "45.45.45.45:8484", "84.84.84.84:8484", } for i, n := range things { h, p, err := net.SplitHostPort(n) if err != nil { continue } port := 8484 tmp, err := strconv.Atoi(p) if err == nil { port = tmp } rr := &SRV{ Hdr: RR_Header{Name: "somename.", Rrtype: TypeSRV, Class: ClassINET, Ttl: 5}, Priority: uint16(i), Weight: 5, Port: uint16(port), Target: h + ".", } msg.Answer = append(msg.Answer, rr) } _, err := msg.Pack() if err != nil { t.Fatalf("Couldn't pack %v\n", msg) } } func TestParseBackslash(t *testing.T) { if r, e := NewRR("nul\\000gap.test.globnix.net. 600 IN A 192.0.2.10"); e != nil { t.Fatalf("Could not create RR with \\000 in it") } else { t.Logf("Parsed %s\n", r.String()) } if r, e := NewRR(`nul\000gap.test.globnix.net. 600 IN TXT "Hello\123"`); e != nil { t.Fatalf("Could not create RR with \\000 in it") } else { t.Logf("Parsed %s\n", r.String()) } } func TestILNP(t *testing.T) { tests := []string{ "host1.example.com.\t3600\tIN\tNID\t10 0014:4fff:ff20:ee64", "host1.example.com.\t3600\tIN\tNID\t20 0015:5fff:ff21:ee65", "host2.example.com.\t3600\tIN\tNID\t10 0016:6fff:ff22:ee66", "host1.example.com.\t3600\tIN\tL32\t10 10.1.2.0", "host1.example.com.\t3600\tIN\tL32\t20 10.1.4.0", "host2.example.com.\t3600\tIN\tL32\t10 10.1.8.0", "host1.example.com.\t3600\tIN\tL64\t10 2001:0DB8:1140:1000", "host1.example.com.\t3600\tIN\tL64\t20 2001:0DB8:2140:2000", "host2.example.com.\t3600\tIN\tL64\t10 2001:0DB8:4140:4000", "host1.example.com.\t3600\tIN\tLP\t10 l64-subnet1.example.com.", "host1.example.com.\t3600\tIN\tLP\t10 l64-subnet2.example.com.", "host1.example.com.\t3600\tIN\tLP\t20 l32-subnet1.example.com.", } for _, t1 := range tests { r, e := NewRR(t1) if e != nil { t.Fatalf("An error occured: %s\n", e.Error()) } else { if t1 != r.String() { t.Fatalf("Strings should be equal %s %s", t1, r.String()) } } } } func TestComment(t *testing.T) { // Comments we must see comments := map[string]bool{"; this is comment 1": true, "; this is comment 4": true, "; this is comment 6": true, "; this is comment 7": true, "; this is comment 8": true} zone := ` foo. IN A 10.0.0.1 ; this is comment 1 foo. IN A ( 10.0.0.2 ; this is comment2 ) ; this is comment3 foo. IN A 10.0.0.3 foo. IN A ( 10.0.0.4 ); this is comment 4 foo. IN A 10.0.0.5 ; this is comment5 foo. IN A 10.0.0.6 foo. IN DNSKEY 256 3 5 AwEAAb+8l ; this is comment 6 foo. IN NSEC miek.nl. TXT RRSIG NSEC; this is comment 7 foo. IN TXT "THIS IS TEXT MAN"; this is comment 8 ` for x := range ParseZone(strings.NewReader(zone), ".", "") { if x.Error == nil { if x.Comment != "" { if _, ok := comments[x.Comment]; !ok { t.Logf("wrong comment %s", x.Comment) t.Fail() } } } } } func TestEUIxx(t *testing.T) { tests := map[string]string{ "host.example. IN EUI48 00-00-5e-90-01-2a": "host.example.\t3600\tIN\tEUI48\t00-00-5e-90-01-2a", "host.example. IN EUI64 00-00-5e-ef-00-00-00-2a": "host.example.\t3600\tIN\tEUI64\t00-00-5e-ef-00-00-00-2a", } for i, o := range tests { r, e := NewRR(i) if e != nil { t.Logf("Failed to parse %s: %s\n", i, e.Error()) t.Fail() } if r.String() != o { t.Logf("Want %s, got %s\n", o, r.String()) t.Fail() } } } func TestUserRR(t *testing.T) { tests := map[string]string{ "host.example. IN UID 1234": "host.example.\t3600\tIN\tUID\t1234", "host.example. IN GID 1234556": "host.example.\t3600\tIN\tGID\t1234556", "host.example. IN UINFO \"Miek Gieben\"": "host.example.\t3600\tIN\tUINFO\t\"Miek Gieben\"", } for i, o := range tests { r, e := NewRR(i) if e != nil { t.Logf("Failed to parse %s: %s\n", i, e.Error()) t.Fail() } if r.String() != o { t.Logf("Want %s, got %s\n", o, r.String()) t.Fail() } } } func TestTXT(t *testing.T) { // Test single entry TXT record rr, err := NewRR(`_raop._tcp.local. 60 IN TXT "single value"`) if err != nil { t.Error("Failed to parse single value TXT record", err) } else if rr, ok := rr.(*TXT); !ok { t.Error("Wrong type, record should be of type TXT") } else { if len(rr.Txt) != 1 { t.Error("Bad size of TXT value:", len(rr.Txt)) } else if rr.Txt[0] != "single value" { t.Error("Bad single value") } if rr.String() != `_raop._tcp.local. 60 IN TXT "single value"` { t.Error("Bad representation of TXT record:", rr.String()) } if rr.len() == 10 { t.Error("Bad size of serialized record:", rr.len()) } } // Test multi entries TXT record rr, err = NewRR(`_raop._tcp.local. 60 IN TXT "a=1" "b=2" "c=3" "d=4"`) if err != nil { t.Error("Failed to parse multi-values TXT record", err) } else if rr, ok := rr.(*TXT); !ok { t.Error("Wrong type, record should be of type TXT") } else { if len(rr.Txt) != 4 { t.Error("Bad size of TXT multi-value:", len(rr.Txt)) } else if rr.Txt[0] != "a=1" || rr.Txt[1] != "b=2" || rr.Txt[2] != "c=3" || rr.Txt[3] != "d=4" { t.Error("Bad values in TXT records") } if rr.String() != `_raop._tcp.local. 60 IN TXT "a=1" "b=2" "c=3" "d=4"` { t.Error("Bad representation of TXT multi value record:", rr.String()) } if rr.len() != 44 { t.Error("Bad size of serialized multi value record:", rr.len()) } } } func TestRR(t *testing.T) { rr, err := NewRR("example.com IN TYPE1234 \\# 4 aabbccdd") if err == nil { t.Log("%s\n", rr.String()) } else { t.Error("Failed to parse TYPE1234 RR: ", err.Error()) } rr, err = NewRR("example.com IN TYPE1 \\# 4 0a000001") if err == nil { t.Error("This should not work") } } func TestPTR(t *testing.T) { _, err := NewRR("144.2.0.192.in-addr.arpa. 900 IN PTR ilouse03146p0\\(.example.com.") if err != nil { t.Error("Failed to parse ", err.Error()) } } func TestDigit(t *testing.T) { tests := map[string]byte{ "miek\\000.nl. 100 IN TXT \"A\"": 0, "miek\\001.nl. 100 IN TXT \"A\"": 1, "miek\\254.nl. 100 IN TXT \"A\"": 254, "miek\\255.nl. 100 IN TXT \"A\"": 255, "miek\\256.nl. 100 IN TXT \"A\"": 0, "miek\\257.nl. 100 IN TXT \"A\"": 1, "miek\\004.nl. 100 IN TXT \"A\"": 4, } for s, i := range tests { r, e := NewRR(s) buf := make([]byte, 40) if e != nil { t.Fatalf("Failed to parse %s\n", e.Error()) } PackRR(r, buf, 0, nil, false) t.Logf("%v\n", buf) if buf[5] != i { t.Fatalf("5 pos must be %d, is %d", i, buf[5]) } r1, _, _ := UnpackRR(buf, 0) if r1.Header().Ttl != 100 { t.Fatalf("Ttl should %d, is %d", 100, r1.Header().Ttl) } } } golang-dns-0.0~git20130912/rawmsg.go000066400000000000000000000042741221430110200167460ustar00rootroot00000000000000// 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. package dns // These raw* functions do not use reflection, they directly set the values // in the buffer. There are faster than their reflection counterparts. // RawSetId sets the message id in buf. func rawSetId(msg []byte, i uint16) bool { if len(msg) < 2 { return false } msg[0], msg[1] = packUint16(i) return true } // rawSetQuestionLen sets the length of the question section. func rawSetQuestionLen(msg []byte, i uint16) bool { if len(msg) < 6 { return false } msg[4], msg[5] = packUint16(i) return true } // rawSetAnswerLen sets the lenght of the answer section. func rawSetAnswerLen(msg []byte, i uint16) bool { if len(msg) < 8 { return false } msg[6], msg[7] = packUint16(i) return true } // rawSetsNsLen sets the lenght of the authority section. func rawSetNsLen(msg []byte, i uint16) bool { if len(msg) < 10 { return false } msg[8], msg[9] = packUint16(i) return true } // rawSetExtraLen sets the lenght of the additional section. func rawSetExtraLen(msg []byte, i uint16) bool { if len(msg) < 12 { return false } msg[10], msg[11] = packUint16(i) return true } // rawSetRdlength sets the rdlength in the header of // the RR. The offset 'off' must be positioned at the // start of the header of the RR, 'end' must be the // end of the RR. func rawSetRdlength(msg []byte, off, end int) bool { l := len(msg) Loop: for { if off+1 > l { return false } c := int(msg[off]) off++ switch c & 0xC0 { case 0x00: if c == 0x00 { // End of the domainname break Loop } if off+c > l { return false } off += c case 0xC0: // pointer, next byte included, ends domainname off++ break Loop } } // The domainname has been seen, we at the start of the fixed part in the header. // Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length. off += 2 + 2 + 4 if off+2 > l { return false } //off+1 is the end of the header, 'end' is the end of the rr //so 'end' - 'off+2' is the lenght of the rdata msg[off], msg[off+1] = packUint16(uint16(end - (off + 2))) return true } golang-dns-0.0~git20130912/scanner.go000066400000000000000000000017051221430110200170730ustar00rootroot00000000000000// 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. package dns // Implement a simple scanner, return a byte stream from an io reader. import ( "bufio" "io" "text/scanner" ) type scan struct { src *bufio.Reader position scanner.Position eof bool // Have we just seen a eof } func scanInit(r io.Reader) *scan { s := new(scan) s.src = bufio.NewReader(r) s.position.Line = 1 return s } // tokenText returns the next byte from the input func (s *scan) tokenText() (byte, error) { c, err := s.src.ReadByte() if err != nil { return c, err } // delay the newline handling until the next token is delivered, // fixes off-by-one errors when reporting a parse error. if s.eof == true { s.position.Line++ s.position.Column = 0 s.eof = false } if c == '\n' { s.eof = true return c, nil } s.position.Column++ return c, nil } golang-dns-0.0~git20130912/server.go000066400000000000000000000340511221430110200167500ustar00rootroot00000000000000// 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. // DNS server implementation. package dns import ( "net" "sync" "time" ) type Handler interface { ServeDNS(w ResponseWriter, r *Msg) } // A ResponseWriter interface is used by an DNS handler to // construct an DNS response. type ResponseWriter interface { // RemoteAddr returns the net.Addr of the client that sent the current request. RemoteAddr() net.Addr // WriteMsg writes a reply back to the client. WriteMsg(*Msg) error // Write writes a raw buffer back to the client. Write([]byte) (int, error) // Close closes the connection. Close() error // TsigStatus returns the status of the Tsig. TsigStatus() error // TsigTimersOnly sets the tsig timers only boolean. TsigTimersOnly(bool) // Hijack lets the caller take over the connection. // After a call to Hijack(), the DNS package will not do anything with the connection Hijack() } type response struct { hijacked bool // connection has been hijacked by handler tsigStatus error tsigTimersOnly bool tsigRequestMAC string tsigSecret map[string]string // the tsig secrets udp *net.UDPConn // i/o connection if UDP was used tcp *net.TCPConn // i/o connection if TCP was used remoteAddr net.Addr // address of the client } // ServeMux is an DNS request multiplexer. It matches the // zone name of each incoming request against a list of // registered patterns add calls the handler for the pattern // that most closely matches the zone name. ServeMux is DNSSEC aware, meaning // that queries for the DS record are redirected to the parent zone (if that // is also registered), otherwise the child gets the query. // ServeMux is also safe for concurrent access from multiple goroutines. type ServeMux struct { z map[string]Handler m *sync.RWMutex } // NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return &ServeMux{z: make(map[string]Handler), m: new(sync.RWMutex)} } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = NewServeMux() // Authors is a list of authors that helped create or make Go DNS better. var Authors = []string{"Miek Gieben", "Ask Bjørn Hansen", "Dave Cheney", "Dusty Wilson", "Peter van Dijk"} // Version holds the current version. var Version = "v1.2" // The HandlerFunc type is an adapter to allow the use of // ordinary functions as DNS handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Msg) // ServerDNS calls f(w, r) func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) { f(w, r) } // FailedHandler returns a HandlerFunc // returns SERVFAIL for every request it gets. func HandleFailed(w ResponseWriter, r *Msg) { m := new(Msg) m.SetRcode(r, RcodeServerFailure) // does not matter if this write fails w.WriteMsg(m) } // AuthorHandler returns a HandlerFunc that returns the authors // of Go DNS for 'authors.bind' or 'authors.server' queries in the // CHAOS Class. Note with: // // dns.HandleFunc("authors.bind.", dns.HandleAuthors) // // the handler is registered for all DNS classes, thereby potentially // hijacking the authors.bind. zone in the IN class. If you need the // authors.bind zone to exist in the IN class, you need to register // some other handler, check the class in there and then call HandleAuthors. func HandleAuthors(w ResponseWriter, r *Msg) { if len(r.Question) != 1 { HandleFailed(w, r) return } if r.Question[0].Qtype != ClassCHAOS && r.Question[0].Qtype != TypeTXT { HandleFailed(w, r) return } if r.Question[0].Name != "authors.server." && r.Question[0].Name != "authors.bind." { HandleFailed(w, r) return } m := new(Msg) m.SetReply(r) for _, author := range Authors { h := RR_Header{r.Question[0].Name, TypeTXT, ClassCHAOS, 0, 0} m.Answer = append(m.Answer, &TXT{h, []string{author}}) } w.WriteMsg(m) } // VersionHandler returns a HandlerFunc that returns the version // of Go DNS for 'version.bind' or 'version.server' queries in the // CHAOS Class. Note with: // // dns.HandleFunc("version.bind.", dns.HandleVersion) // // the handler is registered for all DNS classes, thereby potentially // hijacking the version.bind. zone in the IN class. If you need the // version.bind zone to exist in the IN class, you need to register // some other handler, check the class in there and then call HandleVersion. func HandleVersion(w ResponseWriter, r *Msg) { if len(r.Question) != 1 { HandleFailed(w, r) return } if r.Question[0].Qtype != ClassCHAOS && r.Question[0].Qtype != TypeTXT { HandleFailed(w, r) return } if r.Question[0].Name != "version.server." && r.Question[0].Name != "version.bind." { HandleFailed(w, r) return } m := new(Msg) m.SetReply(r) h := RR_Header{r.Question[0].Name, TypeTXT, ClassCHAOS, 0, 0} m.Answer = append(m.Answer, &TXT{h, []string{Version}}) w.WriteMsg(m) } func authorHandler() Handler { return HandlerFunc(HandleAuthors) } func failedHandler() Handler { return HandlerFunc(HandleFailed) } func versionHandler() Handler { return HandlerFunc(HandleVersion) } // Start a server on addresss and network speficied. Invoke handler // for incoming queries. func ListenAndServe(addr string, network string, handler Handler) error { server := &Server{Addr: addr, Net: network, Handler: handler} return server.ListenAndServe() } func (mux *ServeMux) match(q string, t uint16) Handler { mux.m.RLock() defer mux.m.RUnlock() var handler Handler b := make([]byte, len(q)) // worst case, one label of length q off := 0 end := false for { l := len(q[off:]) for i := 0; i < l; i++ { b[i] = q[off+i] | ('a' - 'A') } if h, ok := mux.z[string(b[:l])]; ok { // 'causes garbage, might want to change the map key if t != TypeDS { return h } else { // Continue for DS to see if we have a parent too, if so delegeate to the parent handler = h } } off, end = NextLabel(q, off) if end { break } } // Wildcard match, if we have found nothing try the root zone as a last resort. if h, ok := mux.z["."]; ok { return h } return handler } // Handle adds a handler to the ServeMux for pattern. func (mux *ServeMux) Handle(pattern string, handler Handler) { if pattern == "" { panic("dns: invalid pattern " + pattern) } mux.m.Lock() mux.z[Fqdn(pattern)] = handler mux.m.Unlock() } // Handle adds a handler to the ServeMux for pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { mux.Handle(pattern, HandlerFunc(handler)) } // HandleRemove deregistrars the handler specific for pattern from the ServeMux. func (mux *ServeMux) HandleRemove(pattern string) { if pattern == "" { panic("dns: invalid pattern " + pattern) } mux.m.Lock() delete(mux.z, Fqdn(pattern)) mux.m.Unlock() } // ServeDNS dispatches the request to the handler whose // pattern most closely matches the request message. If DefaultServeMux // is used the correct thing for DS queries is done: a possible parent // is sought. // If no handler is found a standard SERVFAIL message is returned // If the request message does not have a single question in the // question section a SERVFAIL is returned. func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) { var h Handler if len(request.Question) != 1 { h = failedHandler() } else { if h = mux.match(request.Question[0].Name, request.Question[0].Qtype); h == nil { h = failedHandler() } } h.ServeDNS(w, request) } // Handle registers the handler with the given pattern // in the DefaultServeMux. The documentation for // ServeMux explains how patterns are matched. func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } // HandleRemove deregisters the handle with the given pattern // in the DefaultServeMux. func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) } // HandleFunc registers the handler function with the given pattern // in the DefaultServeMux. func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { DefaultServeMux.HandleFunc(pattern, handler) } // A Server defines parameters for running an DNS server. type Server struct { Addr string // address to listen on, ":dns" if empty Net string // if "tcp" it will invoke a TCP listener, otherwise an UDP one Handler Handler // handler to invoke, dns.DefaultServeMux if nil UDPSize int // default buffer size to use to read incoming UDP messages ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections TsigSecret map[string]string // secret(s) for Tsig map[] } // ListenAndServe starts a nameserver on the configured address in *Server. func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":domain" } switch srv.Net { case "tcp", "tcp4", "tcp6": a, e := net.ResolveTCPAddr(srv.Net, addr) if e != nil { return e } l, e := net.ListenTCP(srv.Net, a) if e != nil { return e } return srv.serveTCP(l) case "udp", "udp4", "udp6": a, e := net.ResolveUDPAddr(srv.Net, addr) if e != nil { return e } l, e := net.ListenUDP(srv.Net, a) if e != nil { return e } return srv.serveUDP(l) } return &Error{err: "bad network"} } // serveTCP starts a TCP listener for the server. // Each request is handled in a seperate goroutine. func (srv *Server) serveTCP(l *net.TCPListener) error { defer l.Close() handler := srv.Handler if handler == nil { handler = DefaultServeMux } forever: for { rw, e := l.AcceptTCP() if e != nil { // don't bail out, but wait for a new request continue } if srv.ReadTimeout != 0 { rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) } if srv.WriteTimeout != 0 { rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout)) } l := make([]byte, 2) n, err := rw.Read(l) if err != nil || n != 2 { continue } length, _ := unpackUint16(l, 0) if length == 0 { continue } m := make([]byte, int(length)) n, err = rw.Read(m[:int(length)]) if err != nil || n == 0 { continue } i := n for i < int(length) { j, err := rw.Read(m[i:int(length)]) if err != nil { continue forever } i += j } n = i go serve(rw.RemoteAddr(), handler, m, nil, rw, srv.TsigSecret) } panic("dns: not reached") } // serveUDP starts a UDP listener for the server. // Each request is handled in a seperate goroutine. func (srv *Server) serveUDP(l *net.UDPConn) error { defer l.Close() handler := srv.Handler if handler == nil { handler = DefaultServeMux } if srv.UDPSize == 0 { srv.UDPSize = udpMsgSize } for { if srv.ReadTimeout != 0 { l.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) } if srv.WriteTimeout != 0 { l.SetWriteDeadline(time.Now().Add(srv.WriteTimeout)) } m := make([]byte, srv.UDPSize) n, a, e := l.ReadFromUDP(m) if e != nil || n == 0 { // don't bail out, but wait for a new request continue } m = m[:n] go serve(a, handler, m, l, nil, srv.TsigSecret) } panic("dns: not reached") } // Serve a new connection. func serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, t *net.TCPConn, tsigSecret map[string]string) { // for block to make it easy to break out to close the tcp connection for { // Request has been read in serveUDP or serveTCP w := new(response) w.tsigSecret = tsigSecret w.udp = u w.tcp = t w.remoteAddr = a req := new(Msg) if req.Unpack(m) != nil { // Send a format error back x := new(Msg) x.SetRcodeFormatError(req) w.WriteMsg(x) break } w.tsigStatus = nil if w.tsigSecret != nil { if t := req.IsTsig(); t != nil { secret := t.Hdr.Name if _, ok := tsigSecret[secret]; !ok { w.tsigStatus = ErrKeyAlg } w.tsigStatus = TsigVerify(m, tsigSecret[secret], "", false) w.tsigTimersOnly = false w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC } } h.ServeDNS(w, req) // this does the writing back to the client if w.hijacked { // client takes care of the connection, i.e. calls Close() break } if t != nil { w.Close() } break } return } // WriteMsg implements the ResponseWriter.WriteMsg method. func (w *response) WriteMsg(m *Msg) (err error) { var data []byte if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check) if t := m.IsTsig(); t != nil { data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly) if err != nil { return err } _, err = w.Write(data) return err } } data, err = m.Pack() if err != nil { return err } _, err = w.Write(data) return err } // Write implements the ResponseWriter.Write method. func (w *response) Write(m []byte) (int, error) { switch { case w.udp != nil: n, err := w.udp.WriteTo(m, w.remoteAddr) return n, err case w.tcp != nil: lm := len(m) if len(m) > MaxMsgSize { return 0, &Error{err: "message too large"} } l := make([]byte, 2) l[0], l[1] = packUint16(uint16(lm)) m = append(l, m...) n, err := w.tcp.Write(m) if err != nil { return n, err } i := n if i < lm { j, err := w.tcp.Write(m[i:lm]) if err != nil { return i, err } i += j } n = i return i, nil } panic("not reached") } // RemoteAddr implements the ResponseWriter.RemoteAddr method. func (w *response) RemoteAddr() net.Addr { return w.remoteAddr } // TsigStatus implements the ResponseWriter.TsigStatus method. func (w *response) TsigStatus() error { return w.tsigStatus } // TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method. func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b } // Hijack implements the ResponseWriter.Hijack method. func (w *response) Hijack() { w.hijacked = true } // Close implements the ResponseWriter.Close method func (w *response) Close() error { if w.udp != nil { e := w.udp.Close() w.udp = nil return e } if w.tcp != nil { e := w.tcp.Close() w.tcp = nil return e } // no-op return nil } golang-dns-0.0~git20130912/server_test.go000066400000000000000000000067751221430110200200230ustar00rootroot00000000000000// 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. package dns import ( "runtime" "testing" "time" ) func HelloServer(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} w.WriteMsg(m) } func AnotherHelloServer(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}} w.WriteMsg(m) } func TestServing(t *testing.T) { HandleFunc("miek.nl.", HelloServer) HandleFunc("example.com.", AnotherHelloServer) go func() { err := ListenAndServe(":8053", "udp", nil) if err != nil { t.Log("ListenAndServe: ", err.Error()) t.Fail() } }() time.Sleep(4e8) c := new(Client) m := new(Msg) m.SetQuestion("miek.nl.", TypeTXT) r, _, _ := c.Exchange(m, "127.0.0.1:8053") txt := r.Extra[0].(*TXT).Txt[0] if txt != "Hello world" { t.Log("Unexpected result for miek.nl", txt, "!= Hello world") t.Fail() } m.SetQuestion("example.com.", TypeTXT) r, _, _ = c.Exchange(m, "127.0.0.1:8053") txt = r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Log("Unexpected result for example.com", txt, "!= Hello example") t.Fail() } // Test Mixes cased as notices by Ask. m.SetQuestion("eXaMplE.cOm.", TypeTXT) r, _, _ = c.Exchange(m, "127.0.0.1:8053") txt = r.Extra[0].(*TXT).Txt[0] if txt != "Hello example" { t.Log("Unexpected result for example.com", txt, "!= Hello example") t.Fail() } } func BenchmarkServing(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServer) a := runtime.GOMAXPROCS(4) go func() { ListenAndServe("127.0.0.1:8053", "udp", nil) }() c := new(Client) m := new(Msg) m.SetQuestion("miek.nl", TypeSOA) b.StartTimer() for i := 0; i < b.N; i++ { c.Exchange(m, "127.0.0.1:8053") } runtime.GOMAXPROCS(a) } func HelloServerCompress(w ResponseWriter, req *Msg) { m := new(Msg) m.SetReply(req) m.Extra = make([]RR, 1) m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}} m.Compress = true w.WriteMsg(m) } func BenchmarkServingCompress(b *testing.B) { b.StopTimer() HandleFunc("miek.nl.", HelloServerCompress) a := runtime.GOMAXPROCS(4) go func() { ListenAndServe("127.0.0.1:8053", "udp", nil) }() c := new(Client) m := new(Msg) m.SetQuestion("miek.nl", TypeSOA) b.StartTimer() for i := 0; i < b.N; i++ { c.Exchange(m, "127.0.0.1:8053") } runtime.GOMAXPROCS(a) } func TestDotAsCatchAllWildcard(t *testing.T) { mux := NewServeMux() mux.Handle(".", HandlerFunc(HelloServer)) mux.Handle("example.com.", HandlerFunc(AnotherHelloServer)) handler := mux.match("www.miek.nl.", TypeTXT) if handler == nil { t.Error("wildcard match failed") } handler = mux.match("www.example.com.", TypeTXT) if handler == nil { t.Error("example.com match failed") } handler = mux.match("a.www.example.com.", TypeTXT) if handler == nil { t.Error("a.www.example.com match failed") } handler = mux.match("boe.", TypeTXT) if handler == nil { t.Error("boe. match failed") } } func TestRootServer(t *testing.T) { mux := NewServeMux() mux.Handle(".", HandlerFunc(HelloServer)) handler := mux.match(".", TypeNS) if handler == nil { t.Error("root match failed") } } golang-dns-0.0~git20130912/singleinflight.go000066400000000000000000000027111221430110200204460ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Adapted for dns package usage by Miek Gieben. package dns import "sync" import "time" // call is an in-flight or completed singleflight.Do call type call struct { wg sync.WaitGroup val *Msg rtt time.Duration err error dups int } // singleflight represents a class of work and forms a namespace in // which units of work can be executed with duplicate suppression. type singleflight struct { sync.Mutex // protects m m map[string]*call // lazily initialized } // Do executes and returns the results of the given function, making // sure that only one execution is in-flight for a given key at a // time. If a duplicate comes in, the duplicate caller waits for the // original to complete and receives the same results. // The return value shared indicates whether v was given to multiple callers. func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) { g.Lock() if g.m == nil { g.m = make(map[string]*call) } if c, ok := g.m[key]; ok { c.dups++ g.Unlock() c.wg.Wait() return c.val, c.rtt, c.err, true } c := new(call) c.wg.Add(1) g.m[key] = c g.Unlock() c.val, c.rtt, c.err = fn() c.wg.Done() g.Lock() delete(g.m, key) g.Unlock() return c.val, c.rtt, c.err, c.dups > 0 } golang-dns-0.0~git20130912/tlsa.go000066400000000000000000000043331221430110200164050ustar00rootroot00000000000000// 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. package dns import ( "crypto/sha256" "crypto/sha512" "crypto/x509" "encoding/hex" "errors" "io" "net" "strconv" ) // CertificateToDANE converts a certificate to a hex string as used in the TLSA record. func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) { switch matchingType { case 0: switch selector { case 0: return hex.EncodeToString(cert.Raw), nil case 1: return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil } case 1: h := sha256.New() switch selector { case 0: return hex.EncodeToString(cert.Raw), 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: return hex.EncodeToString(cert.Raw), nil case 1: io.WriteString(h, string(cert.RawSubjectPublicKeyInfo)) return hex.EncodeToString(h.Sum(nil)), nil } } return "", errors.New("dns: bad TLSA MatchingType or TLSA Selector") } // Sign creates a TLSA record from an SSL certificate. func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) { r.Hdr.Rrtype = TypeTLSA r.Usage = uint8(usage) r.Selector = uint8(selector) r.MatchingType = uint8(matchingType) r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert) if err != nil { return err } return nil } // Verify verifies a TLSA record against an SSL certificate. If it is OK // a nil error is returned. func (r *TLSA) Verify(cert *x509.Certificate) error { c, err := CertificateToDANE(r.Selector, r.MatchingType, cert) if err != nil { return err // Not also ErrSig? } if r.Certificate == c { return nil } return ErrSig // ErrSig, really? } // TLSAName returns the ownername of a TLSA resource record as per the // rules specified in RFC 6698, Section 3. func TLSAName(name, service, network string) (string, error) { if !IsFqdn(name) { return "", ErrFqdn } p, e := net.LookupPort(network, service) if e != nil { return "", e } return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil } golang-dns-0.0~git20130912/tsig.go000066400000000000000000000240031221430110200164040ustar00rootroot00000000000000// 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. // TRANSACTION SIGNATURE (TSIG) // // An TSIG or transaction signature adds a HMAC TSIG record to each message sent. // The supported algorithms include: HmacMD5, HmacSHA1 and HmacSHA256. // // 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 AXFR (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 85.223.71.124 // // c := new(dns.Client) // c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} // m := new(dns.Msg) // m.SetAxfr("miek.nl.") // m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) // t, err := c.TransferIn(m, "85.223.71.124:53") // for r := range t { /* ... */ } // // You can now read the records from the AXFR 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(Msg) // m.SetReply(r) // if r.IsTsig() { // if w.TsigStatus() == nil { // // *Msg r has an TSIG record and it was validated // m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) // } else { // // *Msg r has an TSIG records and it was not valided // } // } // w.WriteMsg(m) // } package dns import ( "crypto/hmac" "crypto/md5" "crypto/sha1" "crypto/sha256" "encoding/hex" "hash" "io" "strconv" "strings" "time" ) // HMAC hashing codes. These are transmitted as domain names. const ( HmacMD5 = "hmac-md5.sig-alg.reg.int." HmacSHA1 = "hmac-sha1." HmacSHA256 = "hmac-sha256." ) type TSIG struct { Hdr RR_Header Algorithm string `dns:"domain-name"` TimeSigned uint64 `dns:"uint48"` Fudge uint16 MACSize uint16 MAC string `dns:"size-hex"` OrigId uint16 Error uint16 OtherLen uint16 OtherData string `dns:"size-hex"` } func (rr *TSIG) Header() *RR_Header { return &rr.Hdr } // TSIG has no official presentation format, but this will suffice. func (rr *TSIG) String() string { s := "\n;; TSIG PSEUDOSECTION:\n" s += rr.Hdr.String() + " " + rr.Algorithm + " " + tsigTimeToString(rr.TimeSigned) + " " + strconv.Itoa(int(rr.Fudge)) + " " + strconv.Itoa(int(rr.MACSize)) + " " + strings.ToUpper(rr.MAC) + " " + strconv.Itoa(int(rr.OrigId)) + " " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR " " + strconv.Itoa(int(rr.OtherLen)) + " " + rr.OtherData return s } func (rr *TSIG) len() int { return rr.Hdr.len() + len(rr.Algorithm) + 1 + 6 + 4 + len(rr.MAC)/2 + 1 + 6 + len(rr.OtherData)/2 + 1 } func (rr *TSIG) copy() RR { return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData} } // The following values must be put in wireformat, so that the MAC can be calculated. // RFC 2845, section 3.4.2. TSIG Variables. type tsigWireFmt struct { // From RR_Header Name string `dns:"domain-name"` Class uint16 Ttl uint32 // Rdata of the TSIG Algorithm string `dns:"domain-name"` TimeSigned uint64 `dns:"uint48"` Fudge uint16 // MACSize, MAC and OrigId excluded Error uint16 OtherLen uint16 OtherData string `dns:"size-hex"` } // If we have the MAC use this type to convert it to wiredata. // Section 3.4.3. Request MAC type macWireFmt struct { MACSize uint16 MAC string `dns:"size-hex"` } // 3.3. Time values used in TSIG calculations type timerWireFmt struct { TimeSigned uint64 `dns:"uint48"` Fudge uint16 } // TsigGenerate fills out the TSIG record attached to the message. // The message should contain // a "stub" TSIG RR with the algorithm, key name (owner name of the RR), // time fudge (defaults to 300 seconds) and the current time // The TSIG MAC is saved in that Tsig RR. // When TsigGenerate is called for the first time requestMAC is set to the empty string and // timersOnly is false. // If something goes wrong an error is returned, otherwise it is nil. func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) { if m.IsTsig() == nil { panic("dns: TSIG not last RR in additional") } // If we barf here, the caller is to blame rawsecret, err := packBase64([]byte(secret)) if err != nil { return nil, "", err } rr := m.Extra[len(m.Extra)-1].(*TSIG) m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg mbuf, err := m.Pack() if err != nil { return nil, "", err } buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly) t := new(TSIG) var h hash.Hash switch rr.Algorithm { case HmacMD5: h = hmac.New(md5.New, []byte(rawsecret)) case HmacSHA1: h = hmac.New(sha1.New, []byte(rawsecret)) case HmacSHA256: h = hmac.New(sha256.New, []byte(rawsecret)) default: return nil, "", ErrKeyAlg } io.WriteString(h, string(buf)) t.MAC = hex.EncodeToString(h.Sum(nil)) t.MACSize = uint16(len(t.MAC) / 2) // Size is half! t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0} t.Fudge = rr.Fudge t.TimeSigned = rr.TimeSigned t.Algorithm = rr.Algorithm t.OrigId = m.Id tbuf := make([]byte, t.len()) if off, err := PackRR(t, tbuf, 0, nil, false); err == nil { tbuf = tbuf[:off] // reset to actual size used } else { return nil, "", err } mbuf = append(mbuf, tbuf...) rawSetExtraLen(mbuf, uint16(len(m.Extra)+1)) return mbuf, t.MAC, nil } // TsigVerify verifies the TSIG on a message. // If the signature does not validate err contains the // error, otherwise it is nil. func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { rawsecret, err := packBase64([]byte(secret)) if err != nil { return err } // Srtip the TSIG from the incoming msg stripped, tsig, err := stripTsig(msg) if err != nil { return err } buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly) ti := uint64(time.Now().Unix()) - tsig.TimeSigned if uint64(tsig.Fudge) < ti { return ErrTime } var h hash.Hash switch tsig.Algorithm { case HmacMD5: h = hmac.New(md5.New, []byte(rawsecret)) case HmacSHA1: h = hmac.New(sha1.New, []byte(rawsecret)) case HmacSHA256: h = hmac.New(sha256.New, []byte(rawsecret)) default: return ErrKeyAlg } io.WriteString(h, string(buf)) if strings.ToUpper(hex.EncodeToString(h.Sum(nil))) != strings.ToUpper(tsig.MAC) { return ErrSig } return nil } // Create a wiredata buffer for the MAC calculation. func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte { var buf []byte if rr.TimeSigned == 0 { rr.TimeSigned = uint64(time.Now().Unix()) } if rr.Fudge == 0 { rr.Fudge = 300 // Standard (RFC) default. } if requestMAC != "" { m := new(macWireFmt) m.MACSize = uint16(len(requestMAC) / 2) m.MAC = requestMAC buf = make([]byte, len(requestMAC)) // long enough n, _ := PackStruct(m, buf, 0) buf = buf[:n] } tsigvar := make([]byte, DefaultMsgSize) if timersOnly { tsig := new(timerWireFmt) tsig.TimeSigned = rr.TimeSigned tsig.Fudge = rr.Fudge n, _ := PackStruct(tsig, tsigvar, 0) tsigvar = tsigvar[:n] } else { tsig := new(tsigWireFmt) tsig.Name = strings.ToLower(rr.Hdr.Name) tsig.Class = ClassANY tsig.Ttl = rr.Hdr.Ttl tsig.Algorithm = strings.ToLower(rr.Algorithm) tsig.TimeSigned = rr.TimeSigned tsig.Fudge = rr.Fudge tsig.Error = rr.Error tsig.OtherLen = rr.OtherLen tsig.OtherData = rr.OtherData n, _ := PackStruct(tsig, tsigvar, 0) tsigvar = tsigvar[:n] } if requestMAC != "" { x := append(buf, msgbuf...) buf = append(x, tsigvar...) } else { buf = append(msgbuf, tsigvar...) } return buf } // Strip the TSIG from the raw message func stripTsig(msg []byte) ([]byte, *TSIG, error) { // Copied from msg.go's Unpack() // Header. var dh Header var err error dns := new(Msg) rr := new(TSIG) off := 0 tsigoff := 0 if off, err = UnpackStruct(&dh, msg, off); err != nil { return nil, nil, err } if dh.Arcount == 0 { return nil, nil, ErrNoSig } // Rcode, see msg.go Unpack() if int(dh.Bits&0xF) == RcodeNotAuth { return nil, nil, ErrAuth } // Arrays. dns.Question = make([]Question, dh.Qdcount) dns.Answer = make([]RR, dh.Ancount) dns.Ns = make([]RR, dh.Nscount) dns.Extra = make([]RR, dh.Arcount) for i := 0; i < len(dns.Question); i++ { off, err = UnpackStruct(&dns.Question[i], msg, off) if err != nil { return nil, nil, err } } for i := 0; i < len(dns.Answer); i++ { dns.Answer[i], off, err = UnpackRR(msg, off) if err != nil { return nil, nil, err } } for i := 0; i < len(dns.Ns); i++ { dns.Ns[i], off, err = UnpackRR(msg, off) if err != nil { return nil, nil, err } } for i := 0; i < len(dns.Extra); i++ { tsigoff = off dns.Extra[i], off, err = UnpackRR(msg, off) if err != nil { return nil, nil, err } if dns.Extra[i].Header().Rrtype == TypeTSIG { rr = dns.Extra[i].(*TSIG) // Adjust Arcount. arcount, _ := unpackUint16(msg, 10) msg[10], msg[11] = packUint16(arcount - 1) break } } if rr == nil { return nil, nil, ErrNoSig } return msg[:tsigoff], rr, nil } // Translate the TSIG time signed into a date. There is no // need for RFC1982 calculations as this date is 48 bits. func tsigTimeToString(t uint64) string { ti := time.Unix(int64(t), 0).UTC() return ti.Format("20060102150405") } golang-dns-0.0~git20130912/types.go000066400000000000000000001160741221430110200166140ustar00rootroot00000000000000// 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 package dns import ( "encoding/base64" "fmt" "net" "strconv" "strings" "time" ) type ( Type uint16 Class uint16 ) // Packet formats // Wire constants and supported types. const ( // valid RR_Header.Rrtype and Question.qtype TypeNone uint16 = 0 TypeA uint16 = 1 TypeNS uint16 = 2 TypeMD uint16 = 3 TypeMF uint16 = 4 TypeCNAME uint16 = 5 TypeSOA uint16 = 6 TypeMB uint16 = 7 TypeMG uint16 = 8 TypeMR uint16 = 9 TypeNULL uint16 = 10 TypeWKS uint16 = 11 TypePTR uint16 = 12 TypeHINFO uint16 = 13 TypeMINFO uint16 = 14 TypeMX uint16 = 15 TypeTXT uint16 = 16 TypeRP uint16 = 17 TypeAFSDB uint16 = 18 TypeX25 uint16 = 19 TypeISDN uint16 = 20 TypeRT uint16 = 21 TypeSIG uint16 = 24 TypeKEY uint16 = 25 TypeAAAA uint16 = 28 TypeLOC uint16 = 29 TypeNXT uint16 = 30 TypeSRV uint16 = 33 TypeATMA uint16 = 34 TypeNAPTR uint16 = 35 TypeKX uint16 = 36 TypeCERT uint16 = 37 TypeDNAME uint16 = 39 TypeOPT uint16 = 41 // EDNS TypeDS uint16 = 43 TypeSSHFP uint16 = 44 TypeIPSECKEY uint16 = 45 TypeRRSIG uint16 = 46 TypeNSEC uint16 = 47 TypeDNSKEY uint16 = 48 TypeDHCID uint16 = 49 TypeNSEC3 uint16 = 50 TypeNSEC3PARAM uint16 = 51 TypeTLSA uint16 = 52 TypeHIP uint16 = 55 TypeNINFO uint16 = 56 TypeRKEY uint16 = 57 TypeTALINK uint16 = 58 TypeCDS uint16 = 59 TypeSPF uint16 = 99 TypeUINFO uint16 = 100 TypeUID uint16 = 101 TypeGID uint16 = 102 TypeUNSPEC uint16 = 103 TypeNID uint16 = 104 TypeL32 uint16 = 105 TypeL64 uint16 = 106 TypeLP uint16 = 107 TypeEUI48 uint16 = 108 TypeEUI64 uint16 = 109 TypeTKEY uint16 = 249 TypeTSIG uint16 = 250 // valid Question.Qtype only TypeIXFR uint16 = 251 TypeAXFR uint16 = 252 TypeMAILB uint16 = 253 TypeMAILA uint16 = 254 TypeANY uint16 = 255 TypeURI uint16 = 256 TypeCAA uint16 = 257 TypeTA uint16 = 32768 TypeDLV uint16 = 32769 // valid Question.Qclass ClassINET = 1 ClassCSNET = 2 ClassCHAOS = 3 ClassHESIOD = 4 ClassNONE = 254 ClassANY = 255 // Msg.rcode RcodeSuccess = 0 RcodeFormatError = 1 RcodeServerFailure = 2 RcodeNameError = 3 RcodeNotImplemented = 4 RcodeRefused = 5 RcodeYXDomain = 6 RcodeYXRrset = 7 RcodeNXRrset = 8 RcodeNotAuth = 9 RcodeNotZone = 10 RcodeBadSig = 16 // TSIG RcodeBadVers = 16 // EDNS0 RcodeBadKey = 17 RcodeBadTime = 18 RcodeBadMode = 19 // TKEY RcodeBadName = 20 RcodeBadAlg = 21 RcodeBadTrunc = 22 // TSIG // Opcode OpcodeQuery = 0 OpcodeIQuery = 1 OpcodeStatus = 2 // There is no 3 OpcodeNotify = 4 OpcodeUpdate = 5 ) // The wire format for the DNS packet header. type Header struct { Id uint16 Bits uint16 Qdcount, Ancount, Nscount, Arcount uint16 } const ( // Header.Bits _QR = 1 << 15 // query/response (response=1) _AA = 1 << 10 // authoritative _TC = 1 << 9 // truncated _RD = 1 << 8 // recursion desired _RA = 1 << 7 // recursion available _Z = 1 << 6 // Z _AD = 1 << 5 // authticated data _CD = 1 << 4 // checking disabled _LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. ) // DNS queries. type Question struct { Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed) Qtype uint16 Qclass uint16 } func (q *Question) String() (s string) { // prefix with ; (as in dig) if len(q.Name) == 0 { s = ";.\t" // root label } else { s = ";" + q.Name + "\t" } s += Class(q.Qclass).String() + "\t" s += " " + Type(q.Qtype).String() return s } func (q *Question) len() int { l := len(q.Name) + 1 return l + 4 } type ANY struct { Hdr RR_Header // Does not have any rdata } func (rr *ANY) Header() *RR_Header { return &rr.Hdr } func (rr *ANY) copy() RR { return &ANY{*rr.Hdr.copyHeader()} } func (rr *ANY) String() string { return rr.Hdr.String() } func (rr *ANY) len() int { return rr.Hdr.len() } type CNAME struct { Hdr RR_Header Target string `dns:"cdomain-name"` } func (rr *CNAME) Header() *RR_Header { return &rr.Hdr } func (rr *CNAME) copy() RR { return &CNAME{*rr.Hdr.copyHeader(), rr.Target} } func (rr *CNAME) String() string { return rr.Hdr.String() + rr.Target } func (rr *CNAME) len() int { return rr.Hdr.len() + len(rr.Target) + 1 } type HINFO struct { Hdr RR_Header Cpu string Os string } func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } func (rr *HINFO) copy() RR { return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os} } func (rr *HINFO) String() string { return rr.Hdr.String() + rr.Cpu + " " + rr.Os } func (rr *HINFO) len() int { return rr.Hdr.len() + len(rr.Cpu) + len(rr.Os) } type MB struct { Hdr RR_Header Mb string `dns:"cdomain-name"` } func (rr *MB) Header() *RR_Header { return &rr.Hdr } func (rr *MB) copy() RR { return &MB{*rr.Hdr.copyHeader(), rr.Mb} } func (rr *MB) String() string { return rr.Hdr.String() + rr.Mb } func (rr *MB) len() int { return rr.Hdr.len() + len(rr.Mb) + 1 } type MG struct { Hdr RR_Header Mg string `dns:"cdomain-name"` } func (rr *MG) Header() *RR_Header { return &rr.Hdr } func (rr *MG) copy() RR { return &MG{*rr.Hdr.copyHeader(), rr.Mg} } func (rr *MG) String() string { return rr.Hdr.String() + rr.Mg } func (rr *MG) len() int { l := len(rr.Mg) + 1 return rr.Hdr.len() + l } type MINFO struct { Hdr RR_Header Rmail string `dns:"cdomain-name"` Email string `dns:"cdomain-name"` } func (rr *MINFO) Header() *RR_Header { return &rr.Hdr } func (rr *MINFO) copy() RR { return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email} } func (rr *MINFO) String() string { return rr.Hdr.String() + rr.Rmail + " " + rr.Email } func (rr *MINFO) len() int { l := len(rr.Rmail) + 1 n := len(rr.Email) + 1 return rr.Hdr.len() + l + n } type MR struct { Hdr RR_Header Mr string `dns:"cdomain-name"` } func (rr *MR) Header() *RR_Header { return &rr.Hdr } func (rr *MR) copy() RR { return &MR{*rr.Hdr.copyHeader(), rr.Mr} } func (rr *MR) String() string { return rr.Hdr.String() + rr.Mr } func (rr *MR) len() int { l := len(rr.Mr) + 1 return rr.Hdr.len() + l } type MF struct { Hdr RR_Header Mf string `dns:"cdomain-name"` } func (rr *MF) Header() *RR_Header { return &rr.Hdr } func (rr *MF) copy() RR { return &MF{*rr.Hdr.copyHeader(), rr.Mf} } func (rr *MF) String() string { return rr.Hdr.String() + " " + rr.Mf } func (rr *MF) len() int { return rr.Hdr.len() + len(rr.Mf) + 1 } type MD struct { Hdr RR_Header Md string `dns:"cdomain-name"` } func (rr *MD) Header() *RR_Header { return &rr.Hdr } func (rr *MD) copy() RR { return &MD{*rr.Hdr.copyHeader(), rr.Md} } func (rr *MD) String() string { return rr.Hdr.String() + " " + rr.Md } func (rr *MD) len() int { return rr.Hdr.len() + len(rr.Md) + 1 } type MX struct { Hdr RR_Header Preference uint16 Mx string `dns:"cdomain-name"` } func (rr *MX) Header() *RR_Header { return &rr.Hdr } func (rr *MX) copy() RR { return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx} } func (rr *MX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + rr.Mx } func (rr *MX) len() int { l := len(rr.Mx) + 1 return rr.Hdr.len() + l + 2 } type AFSDB struct { Hdr RR_Header Subtype uint16 Hostname string `dns:"cdomain-name"` } func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } func (rr *AFSDB) copy() RR { return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname} } func (rr *AFSDB) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + rr.Hostname } func (rr *AFSDB) len() int { l := len(rr.Hostname) + 1 return rr.Hdr.len() + l + 2 } type X25 struct { Hdr RR_Header PSDNAddress string } func (rr *X25) Header() *RR_Header { return &rr.Hdr } func (rr *X25) copy() RR { return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress} } func (rr *X25) String() string { return rr.Hdr.String() + rr.PSDNAddress } func (rr *X25) len() int { return rr.Hdr.len() + len(rr.PSDNAddress) } type RT struct { Hdr RR_Header Preference uint16 Host string `dns:"cdomain-name"` } func (rr *RT) Header() *RR_Header { return &rr.Hdr } func (rr *RT) copy() RR { return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host} } func (rr *RT) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + rr.Host } func (rr *RT) len() int { l := len(rr.Host) + 1 return rr.Hdr.len() + l + 2 } type NS struct { Hdr RR_Header Ns string `dns:"cdomain-name"` } func (rr *NS) Header() *RR_Header { return &rr.Hdr } func (rr *NS) copy() RR { return &NS{*rr.Hdr.copyHeader(), rr.Ns} } func (rr *NS) String() string { return rr.Hdr.String() + rr.Ns } func (rr *NS) len() int { l := len(rr.Ns) + 1 return rr.Hdr.len() + l } type PTR struct { Hdr RR_Header Ptr string `dns:"cdomain-name"` } func (rr *PTR) Header() *RR_Header { return &rr.Hdr } func (rr *PTR) copy() RR { return &PTR{*rr.Hdr.copyHeader(), rr.Ptr} } func (rr *PTR) String() string { return rr.Hdr.String() + rr.Ptr } func (rr *PTR) len() int { l := len(rr.Ptr) + 1 return rr.Hdr.len() + l } type RP struct { Hdr RR_Header Mbox string `dns:"domain-name"` Txt string `dns:"domain-name"` } func (rr *RP) Header() *RR_Header { return &rr.Hdr } func (rr *RP) copy() RR { return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt} } func (rr *RP) String() string { return rr.Hdr.String() + rr.Mbox + " " + rr.Txt } func (rr *RP) len() int { return rr.Hdr.len() + len(rr.Mbox) + 1 + len(rr.Txt) + 1 } type SOA struct { Hdr RR_Header Ns string `dns:"cdomain-name"` Mbox string `dns:"cdomain-name"` Serial uint32 Refresh uint32 Retry uint32 Expire uint32 Minttl uint32 } func (rr *SOA) Header() *RR_Header { return &rr.Hdr } func (rr *SOA) copy() RR { return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl} } func (rr *SOA) String() string { return rr.Hdr.String() + rr.Ns + " " + rr.Mbox + " " + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.FormatInt(int64(rr.Refresh), 10) + " " + strconv.FormatInt(int64(rr.Retry), 10) + " " + strconv.FormatInt(int64(rr.Expire), 10) + " " + strconv.FormatInt(int64(rr.Minttl), 10) } func (rr *SOA) len() int { l := len(rr.Ns) + 1 n := len(rr.Mbox) + 1 return rr.Hdr.len() + l + n + 20 } type TXT struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *TXT) Header() *RR_Header { return &rr.Hdr } func (rr *TXT) copy() RR { return &TXT{*rr.Hdr.copyHeader(), rr.Txt} } // this doesn't really copy Txt does it? TODO(mg) func (rr *TXT) String() string { s := rr.Hdr.String() for i, s1 := range rr.Txt { if i > 0 { s += " " + strconv.QuoteToASCII(s1) } else { s += strconv.QuoteToASCII(s1) } } return s } func (rr *TXT) len() int { l := rr.Hdr.len() for _, t := range rr.Txt { l += len(t) + 1 } return l } type SPF struct { Hdr RR_Header Txt []string `dns:"txt"` } func (rr *SPF) Header() *RR_Header { return &rr.Hdr } func (rr *SPF) copy() RR { return &SPF{*rr.Hdr.copyHeader(), rr.Txt} } func (rr *SPF) String() string { s := rr.Hdr.String() for i, s1 := range rr.Txt { if i > 0 { s += " " + strconv.QuoteToASCII(s1) } else { s += strconv.QuoteToASCII(s1) } } return s } func (rr *SPF) len() int { l := rr.Hdr.len() for _, t := range rr.Txt { l += len(t) } return l } type SRV struct { Hdr RR_Header Priority uint16 Weight uint16 Port uint16 Target string `dns:"domain-name"` } func (rr *SRV) Header() *RR_Header { return &rr.Hdr } func (rr *SRV) copy() RR { return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target} } func (rr *SRV) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + " " + strconv.Itoa(int(rr.Weight)) + " " + strconv.Itoa(int(rr.Port)) + " " + rr.Target } func (rr *SRV) len() int { l := len(rr.Target) + 1 return rr.Hdr.len() + l + 6 } type NAPTR struct { Hdr RR_Header Order uint16 Preference uint16 Flags string Service string Regexp string Replacement string `dns:"domain-name"` } func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr } func (rr *NAPTR) copy() RR { return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement} } func (rr *NAPTR) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Order)) + " " + strconv.Itoa(int(rr.Preference)) + " " + "\"" + rr.Flags + "\" " + "\"" + rr.Service + "\" " + "\"" + rr.Regexp + "\" " + rr.Replacement } func (rr *NAPTR) len() int { return rr.Hdr.len() + 4 + len(rr.Flags) + len(rr.Service) + len(rr.Regexp) + len(rr.Replacement) + 1 } // See RFC 4398. type CERT struct { Hdr RR_Header Type uint16 KeyTag uint16 Algorithm uint8 Certificate string `dns:"base64"` } func (rr *CERT) Header() *RR_Header { return &rr.Hdr } func (rr *CERT) copy() RR { return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} } func (rr *CERT) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Type)) + " " + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + rr.Certificate } func (rr *CERT) len() int { return rr.Hdr.len() + 5 + base64.StdEncoding.DecodedLen(len(rr.Certificate)) } // See RFC 2672. type DNAME struct { Hdr RR_Header Target string `dns:"domain-name"` } func (rr *DNAME) Header() *RR_Header { return &rr.Hdr } func (rr *DNAME) copy() RR { return &DNAME{*rr.Hdr.copyHeader(), rr.Target} } func (rr *DNAME) String() string { return rr.Hdr.String() + rr.Target } func (rr *DNAME) len() int { l := len(rr.Target) + 1 return rr.Hdr.len() + l } type A struct { Hdr RR_Header A net.IP `dns:"a"` } func (rr *A) Header() *RR_Header { return &rr.Hdr } func (rr *A) copy() RR { return &A{*rr.Hdr.copyHeader(), rr.A} } func (rr *A) len() int { return rr.Hdr.len() + net.IPv4len } func (rr *A) String() string { if rr.A == nil { return rr.Hdr.String() } return rr.Hdr.String() + rr.A.String() } type AAAA struct { Hdr RR_Header AAAA net.IP `dns:"aaaa"` } func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } func (rr *AAAA) copy() RR { return &AAAA{*rr.Hdr.copyHeader(), rr.AAAA} } func (rr *AAAA) len() int { return rr.Hdr.len() + net.IPv6len } func (rr *AAAA) String() string { if rr.AAAA == nil { return rr.Hdr.String() } return rr.Hdr.String() + rr.AAAA.String() } type LOC struct { Hdr RR_Header Version uint8 Size uint8 HorizPre uint8 VertPre uint8 Latitude uint32 Longitude uint32 Altitude uint32 } func (rr *LOC) Header() *RR_Header { return &rr.Hdr } func (rr *LOC) copy() RR { return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude} } func (rr *LOC) String() string { s := rr.Hdr.String() // Copied from ldns // Latitude lat := rr.Latitude north := "N" if lat > _LOC_EQUATOR { lat = lat - _LOC_EQUATOR } else { north = "S" lat = _LOC_EQUATOR - lat } h := lat / (1000 * 60 * 60) lat = lat % (1000 * 60 * 60) m := lat / (1000 * 60) lat = lat % (1000 * 60) s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float32(lat) / 1000), north) // Longitude lon := rr.Longitude east := "E" if lon > _LOC_EQUATOR { lon = lon - _LOC_EQUATOR } else { east = "W" lon = _LOC_EQUATOR - lon } h = lon / (1000 * 60 * 60) lon = lon % (1000 * 60 * 60) m = lon / (1000 * 60) lon = lon % (1000 * 60) s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float32(lon) / 1000), east) s1 := rr.Altitude / 100.00 s1 -= 100000 if rr.Altitude%100 == 0 { s += fmt.Sprintf("%.2fm ", float32(s1)) } else { s += fmt.Sprintf("%.0fm ", float32(s1)) } s += cmToString((rr.Size&0xf0)>>4, rr.Size&0x0f) + "m " s += cmToString((rr.HorizPre&0xf0)>>4, rr.HorizPre&0x0f) + "m " s += cmToString((rr.VertPre&0xf0)>>4, rr.VertPre&0x0f) + "m" return s } func (rr *LOC) len() int { return rr.Hdr.len() + 4 + 12 } type RRSIG struct { Hdr RR_Header TypeCovered uint16 Algorithm uint8 Labels uint8 OrigTtl uint32 Expiration uint32 Inception uint32 KeyTag uint16 SignerName string `dns:"domain-name"` Signature string `dns:"base64"` } func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr } func (rr *RRSIG) copy() RR { return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature} } func (rr *RRSIG) String() string { s := rr.Hdr.String() s += Type(rr.TypeCovered).String() s += " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.Labels)) + " " + strconv.FormatInt(int64(rr.OrigTtl), 10) + " " + TimeToString(rr.Expiration) + " " + TimeToString(rr.Inception) + " " + strconv.Itoa(int(rr.KeyTag)) + " " + rr.SignerName + " " + rr.Signature return s } func (rr *RRSIG) len() int { return rr.Hdr.len() + len(rr.SignerName) + 1 + base64.StdEncoding.DecodedLen(len(rr.Signature)) + 18 } type NSEC struct { Hdr RR_Header NextDomain string `dns:"domain-name"` TypeBitMap []uint16 `dns:"nsec"` } func (rr *NSEC) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC) copy() RR { return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, rr.TypeBitMap} } func (rr *NSEC) String() string { s := rr.Hdr.String() + rr.NextDomain for i := 0; i < len(rr.TypeBitMap); i++ { s += " " + Type(rr.TypeBitMap[i]).String() } return s } func (rr *NSEC) len() int { l := rr.Hdr.len() + len(rr.NextDomain) + 1 lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 if uint32(window) != lastwindow { l += 1 + 32 } lastwindow = uint32(window) } return l } type DS struct { Hdr RR_Header KeyTag uint16 Algorithm uint8 DigestType uint8 Digest string `dns:"hex"` } func (rr *DS) Header() *RR_Header { return &rr.Hdr } func (rr *DS) copy() RR { return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} } func (rr *DS) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.DigestType)) + " " + strings.ToUpper(rr.Digest) } func (rr *DS) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 } type CDS struct { Hdr RR_Header KeyTag uint16 Algorithm uint8 DigestType uint8 Digest string `dns:"hex"` } func (rr *CDS) Header() *RR_Header { return &rr.Hdr } func (rr *CDS) copy() RR { return &CDS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} } func (rr *CDS) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.DigestType)) + " " + strings.ToUpper(rr.Digest) } func (rr *CDS) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 } type DLV struct { Hdr RR_Header KeyTag uint16 Algorithm uint8 DigestType uint8 Digest string `dns:"hex"` } func (rr *DLV) Header() *RR_Header { return &rr.Hdr } func (rr *DLV) copy() RR { return &DLV{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} } func (rr *DLV) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.DigestType)) + " " + strings.ToUpper(rr.Digest) } func (rr *DLV) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 } type KX struct { Hdr RR_Header Preference uint16 Exchanger string `dns:"domain-name"` } func (rr *KX) Header() *RR_Header { return &rr.Hdr } func (rr *KX) copy() RR { return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger} } func (rr *KX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + rr.Exchanger } func (rr *KX) len() int { return rr.Hdr.len() + 2 + len(rr.Exchanger) } type TA struct { Hdr RR_Header KeyTag uint16 Algorithm uint8 DigestType uint8 Digest string `dns:"hex"` } func (rr *TA) Header() *RR_Header { return &rr.Hdr } func (rr *TA) copy() RR { return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} } func (rr *TA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.DigestType)) + " " + strings.ToUpper(rr.Digest) } func (rr *TA) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 } type TALINK struct { Hdr RR_Header PreviousName string `dns:"domain-name"` NextName string `dns:"domain-name"` } func (rr *TALINK) Header() *RR_Header { return &rr.Hdr } func (rr *TALINK) copy() RR { return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName} } func (rr *TALINK) String() string { return rr.Hdr.String() + " " + rr.PreviousName + " " + rr.NextName } func (rr *TALINK) len() int { return rr.Hdr.len() + len(rr.PreviousName) + len(rr.NextName) + 2 } type SSHFP struct { Hdr RR_Header Algorithm uint8 Type uint8 FingerPrint string `dns:"hex"` } func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr } func (rr *SSHFP) copy() RR { return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint} } func (rr *SSHFP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) + " " + strconv.Itoa(int(rr.Type)) + " " + strings.ToUpper(rr.FingerPrint) } func (rr *SSHFP) len() int { return rr.Hdr.len() + 2 + len(rr.FingerPrint)/2 } type IPSECKEY struct { Hdr RR_Header Precedence uint8 GatewayType uint8 Algorithm uint8 Gateway string `dns:"ipseckey"` PublicKey string `dns:"base64"` } func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr } func (rr *IPSECKEY) copy() RR { return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, rr.Gateway, rr.PublicKey} } func (rr *IPSECKEY) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + " " + strconv.Itoa(int(rr.GatewayType)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + rr.Gateway + " " + rr.PublicKey } func (rr *IPSECKEY) len() int { return rr.Hdr.len() + 3 + len(rr.Gateway) + 1 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) } type DNSKEY struct { Hdr RR_Header Flags uint16 Protocol uint8 Algorithm uint8 PublicKey string `dns:"base64"` } func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr } func (rr *DNSKEY) copy() RR { return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} } func (rr *DNSKEY) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Protocol)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + rr.PublicKey } func (rr *DNSKEY) len() int { return rr.Hdr.len() + 4 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) } type RKEY struct { Hdr RR_Header Flags uint16 Protocol uint8 Algorithm uint8 PublicKey string `dns:"base64"` } func (rr *RKEY) Header() *RR_Header { return &rr.Hdr } func (rr *RKEY) copy() RR { return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} } func (rr *RKEY) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Protocol)) + " " + strconv.Itoa(int(rr.Algorithm)) + " " + rr.PublicKey } func (rr *RKEY) len() int { return rr.Hdr.len() + 4 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) } type NSEC3 struct { Hdr RR_Header Hash uint8 Flags uint8 Iterations uint16 SaltLength uint8 Salt string `dns:"size-hex"` HashLength uint8 NextDomain string `dns:"size-base32"` TypeBitMap []uint16 `dns:"nsec"` } func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC3) copy() RR { return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, rr.TypeBitMap} } func (rr *NSEC3) String() string { s := rr.Hdr.String() s += strconv.Itoa(int(rr.Hash)) + " " + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Iterations)) + " " + saltToString(rr.Salt) + " " + rr.NextDomain for i := 0; i < len(rr.TypeBitMap); i++ { s += " " + Type(rr.TypeBitMap[i]).String() } return s } func (rr *NSEC3) len() int { l := rr.Hdr.len() + 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 if uint32(window) != lastwindow { l += 1 + 32 } lastwindow = uint32(window) } return l } type NSEC3PARAM struct { Hdr RR_Header Hash uint8 Flags uint8 Iterations uint16 SaltLength uint8 Salt string `dns:"hex"` } func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr } func (rr *NSEC3PARAM) copy() RR { return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt} } func (rr *NSEC3PARAM) String() string { s := rr.Hdr.String() s += strconv.Itoa(int(rr.Hash)) + " " + strconv.Itoa(int(rr.Flags)) + " " + strconv.Itoa(int(rr.Iterations)) + " " + saltToString(rr.Salt) return s } func (rr *NSEC3PARAM) len() int { return rr.Hdr.len() + 2 + 4 + 1 + len(rr.Salt)/2 } type TKEY struct { Hdr RR_Header Algorithm string `dns:"domain-name"` Inception uint32 Expiration uint32 Mode uint16 Error uint16 KeySize uint16 Key string OtherLen uint16 OtherData string } func (rr *TKEY) Header() *RR_Header { return &rr.Hdr } func (rr *TKEY) copy() RR { return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData} } func (rr *TKEY) String() string { // It has no presentation format return "" } func (rr *TKEY) len() int { return rr.Hdr.len() + len(rr.Algorithm) + 1 + 4 + 4 + 6 + len(rr.Key) + 2 + len(rr.OtherData) } // RFC3597 representes an unknown RR. type RFC3597 struct { Hdr RR_Header Rdata string `dns:"hex"` } func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr } func (rr *RFC3597) copy() RR { return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata} } func (rr *RFC3597) String() string { s := rr.Hdr.String() s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata return s } func (rr *RFC3597) len() int { return rr.Hdr.len() + len(rr.Rdata)/2 } type URI struct { Hdr RR_Header Priority uint16 Weight uint16 Target []string `dns:"txt"` } func (rr *URI) Header() *RR_Header { return &rr.Hdr } func (rr *URI) copy() RR { return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, rr.Target} } func (rr *URI) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + " " + strconv.Itoa(int(rr.Weight)) for i, s1 := range rr.Target { if i > 0 { s += " " + strconv.QuoteToASCII(s1) } else { s += strconv.QuoteToASCII(s1) } } return s } func (rr *URI) len() int { return rr.Hdr.len() + 4 + len(rr.Target) + 1 } type DHCID struct { Hdr RR_Header Digest string `dns:"base64"` } func (rr *DHCID) Header() *RR_Header { return &rr.Hdr } func (rr *DHCID) copy() RR { return &DHCID{*rr.Hdr.copyHeader(), rr.Digest} } func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest } func (rr *DHCID) len() int { return rr.Hdr.len() + base64.StdEncoding.DecodedLen(len(rr.Digest)) } type TLSA struct { Hdr RR_Header Usage uint8 Selector uint8 MatchingType uint8 Certificate string `dns:"hex"` } func (rr *TLSA) Header() *RR_Header { return &rr.Hdr } func (rr *TLSA) copy() RR { return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} } func (rr *TLSA) String() string { return rr.Hdr.String() + " " + strconv.Itoa(int(rr.Usage)) + " " + strconv.Itoa(int(rr.Selector)) + " " + strconv.Itoa(int(rr.MatchingType)) + " " + rr.Certificate } func (rr *TLSA) len() int { return rr.Hdr.len() + 3 + len(rr.Certificate)/2 } type HIP struct { Hdr RR_Header HitLength uint8 PublicKeyAlgorithm uint8 PublicKeyLength uint16 Hit string `dns:"hex"` PublicKey string `dns:"base64"` RendezvousServers []string `dns:"domain-name"` } func (rr *HIP) Header() *RR_Header { return &rr.Hdr } func (rr *HIP) copy() RR { return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, rr.RendezvousServers} } func (rr *HIP) String() string { s := rr.Hdr.String() + " " + strconv.Itoa(int(rr.PublicKeyAlgorithm)) + " " + rr.Hit + " " + rr.PublicKey for _, d := range rr.RendezvousServers { s += " " + d } return s } func (rr *HIP) len() int { l := rr.Hdr.len() + 4 + len(rr.Hit)/2 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) for _, d := range rr.RendezvousServers { l += len(d) + 1 } return l } type NINFO struct { Hdr RR_Header ZSData []string `dns:"txt"` } func (rr *NINFO) Header() *RR_Header { return &rr.Hdr } func (rr *NINFO) copy() RR { return &NINFO{*rr.Hdr.copyHeader(), rr.ZSData} } func (rr *NINFO) String() string { s := rr.Hdr.String() for i, s1 := range rr.ZSData { if i > 0 { s += " " + strconv.QuoteToASCII(s1) } else { s += strconv.QuoteToASCII(s1) } } return s } func (rr *NINFO) len() int { l := rr.Hdr.len() for _, t := range rr.ZSData { l += len(t) } return l } type WKS struct { Hdr RR_Header Address net.IP `dns:"a"` Protocol uint8 BitMap []uint16 `dns:"wks"` } func (rr *WKS) Header() *RR_Header { return &rr.Hdr } func (rr *WKS) copy() RR { return &WKS{*rr.Hdr.copyHeader(), rr.Address, rr.Protocol, rr.BitMap} } func (rr *WKS) String() (s string) { s = rr.Hdr.String() if rr.Address != nil { s += rr.Address.String() } for i := 0; i < len(rr.BitMap); i++ { // should lookup the port s += " " + strconv.Itoa(int(rr.BitMap[i])) } return s } func (rr *WKS) len() int { return rr.Hdr.len() + net.IPv4len + 1 } type NID struct { Hdr RR_Header Preference uint16 NodeID uint64 } func (rr *NID) Header() *RR_Header { return &rr.Hdr } func (rr *NID) copy() RR { return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID} } func (rr *NID) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) node := fmt.Sprintf("%0.16x", rr.NodeID) s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16] return s } func (rr *NID) len() int { return rr.Hdr.len() + 2 + 8 } type L32 struct { Hdr RR_Header Preference uint16 Locator32 net.IP `dns:"a"` } func (rr *L32) Header() *RR_Header { return &rr.Hdr } func (rr *L32) copy() RR { return &L32{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator32} } func (rr *L32) String() string { if rr.Locator32 == nil { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) } return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + rr.Locator32.String() } func (rr *L32) len() int { return rr.Hdr.len() + net.IPv4len } type L64 struct { Hdr RR_Header Preference uint16 Locator64 uint64 } func (rr *L64) Header() *RR_Header { return &rr.Hdr } func (rr *L64) copy() RR { return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64} } func (rr *L64) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) node := fmt.Sprintf("%0.16X", rr.Locator64) s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16] return s } func (rr *L64) len() int { return rr.Hdr.len() + 2 + 8 } type LP struct { Hdr RR_Header Preference uint16 Fqdn string `dns:"domain-name"` } func (rr *LP) Header() *RR_Header { return &rr.Hdr } func (rr *LP) copy() RR { return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn} } func (rr *LP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + rr.Fqdn } func (rr *LP) len() int { return rr.Hdr.len() + 2 + len(rr.Fqdn) + 1 } type EUI48 struct { Hdr RR_Header Address uint64 `dns:"uint48"` } func (rr *EUI48) Header() *RR_Header { return &rr.Hdr } func (rr *EUI48) copy() RR { return &EUI48{*rr.Hdr.copyHeader(), rr.Address} } func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) } func (rr *EUI48) len() int { return rr.Hdr.len() + 6 } type EUI64 struct { Hdr RR_Header Address uint64 } func (rr *EUI64) Header() *RR_Header { return &rr.Hdr } func (rr *EUI64) copy() RR { return &EUI64{*rr.Hdr.copyHeader(), rr.Address} } func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) } func (rr *EUI64) len() int { return rr.Hdr.len() + 8 } type CAA struct { Hdr RR_Header Flag uint8 Tag string Value []string `dns:"txt"` } func (rr *CAA) Header() *RR_Header { return &rr.Hdr } func (rr *CAA) copy() RR { return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} } func (rr *CAA) String() string { s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Flag), 10) + " " + rr.Tag for i, s1 := range rr.Value { if i > 0 { s += " " + strconv.QuoteToASCII(s1) } else { s += strconv.QuoteToASCII(s1) } } return s } func (rr *CAA) len() int { l := rr.Hdr.len() + 1 + len(rr.Tag) for _, t := range rr.Value { l += len(t) } return l } type UID struct { Hdr RR_Header Uid uint32 } func (rr *UID) Header() *RR_Header { return &rr.Hdr } func (rr *UID) copy() RR { return &UID{*rr.Hdr.copyHeader(), rr.Uid} } func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) } func (rr *UID) len() int { return rr.Hdr.len() + 4 } type GID struct { Hdr RR_Header Gid uint32 } func (rr *GID) Header() *RR_Header { return &rr.Hdr } func (rr *GID) copy() RR { return &GID{*rr.Hdr.copyHeader(), rr.Gid} } func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) } func (rr *GID) len() int { return rr.Hdr.len() + 4 } type UINFO struct { Hdr RR_Header Uinfo string } func (rr *UINFO) Header() *RR_Header { return &rr.Hdr } func (rr *UINFO) copy() RR { return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo} } func (rr *UINFO) String() string { return rr.Hdr.String() + strconv.QuoteToASCII(rr.Uinfo) } func (rr *UINFO) len() int { return rr.Hdr.len() + len(rr.Uinfo) + 1 } // TimeToString translates the RRSIG's incep. and expir. times to the // string representation used when printing the record. // It takes serial arithmetic (RFC 1982) into account. func TimeToString(t uint32) string { mod := ((int64(t) - time.Now().Unix()) / year68) - 1 if mod < 0 { mod = 0 } ti := time.Unix(int64(t)-(mod*year68), 0).UTC() return ti.Format("20060102150405") } // StringToTime translates the RRSIG's incep. and expir. times from // string values like "20110403154150" to an 32 bit integer. // It takes serial arithmetic (RFC 1982) into account. func StringToTime(s string) (uint32, error) { t, e := time.Parse("20060102150405", s) if e != nil { return 0, e } mod := (t.Unix() / year68) - 1 if mod < 0 { mod = 0 } return uint32(t.Unix() - (mod * year68)), nil } // saltToString converts a NSECX salt to uppercase and // returns "-" when it is empty func saltToString(s string) string { if len(s) == 0 { return "-" } return strings.ToUpper(s) } func cmToString(mantissa, exponent uint8) string { switch exponent { case 0, 1: if exponent == 1 { mantissa *= 10 } return fmt.Sprintf("%.02f", float32(mantissa)) default: s := fmt.Sprintf("%d", mantissa) for i := uint8(0); i < exponent-2; i++ { s += "0" } return s } panic("dns: not reached") } func euiToString(eui uint64, bits int) (hex string) { switch bits { case 64: hex = fmt.Sprintf("%16.16x", eui) hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] + "-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16] case 48: hex = fmt.Sprintf("%12.12x", eui) hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] + "-" + hex[8:10] + "-" + hex[10:12] } return } // Map of constructors for each RR wire type. var rr_mk = map[uint16]func() RR{ TypeCNAME: func() RR { return new(CNAME) }, TypeHINFO: func() RR { return new(HINFO) }, TypeMB: func() RR { return new(MB) }, TypeMG: func() RR { return new(MG) }, TypeMD: func() RR { return new(MD) }, TypeMF: func() RR { return new(MF) }, TypeMINFO: func() RR { return new(MINFO) }, TypeRP: func() RR { return new(RP) }, TypeAFSDB: func() RR { return new(AFSDB) }, TypeX25: func() RR { return new(X25) }, TypeMR: func() RR { return new(MR) }, TypeMX: func() RR { return new(MX) }, TypeRKEY: func() RR { return new(RKEY) }, TypeNINFO: func() RR { return new(NINFO) }, TypeNS: func() RR { return new(NS) }, TypePTR: func() RR { return new(PTR) }, TypeSOA: func() RR { return new(SOA) }, TypeRT: func() RR { return new(RT) }, TypeTXT: func() RR { return new(TXT) }, TypeSRV: func() RR { return new(SRV) }, TypeNAPTR: func() RR { return new(NAPTR) }, TypeDNAME: func() RR { return new(DNAME) }, TypeA: func() RR { return new(A) }, TypeWKS: func() RR { return new(WKS) }, TypeAAAA: func() RR { return new(AAAA) }, TypeLOC: func() RR { return new(LOC) }, TypeOPT: func() RR { return new(OPT) }, TypeDS: func() RR { return new(DS) }, TypeCDS: func() RR { return new(CDS) }, TypeCERT: func() RR { return new(CERT) }, TypeKX: func() RR { return new(KX) }, TypeSPF: func() RR { return new(SPF) }, TypeTALINK: func() RR { return new(TALINK) }, TypeSSHFP: func() RR { return new(SSHFP) }, TypeRRSIG: func() RR { return new(RRSIG) }, TypeNSEC: func() RR { return new(NSEC) }, TypeDNSKEY: func() RR { return new(DNSKEY) }, TypeNSEC3: func() RR { return new(NSEC3) }, TypeDHCID: func() RR { return new(DHCID) }, TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, TypeTKEY: func() RR { return new(TKEY) }, TypeTSIG: func() RR { return new(TSIG) }, TypeURI: func() RR { return new(URI) }, TypeTA: func() RR { return new(TA) }, TypeDLV: func() RR { return new(DLV) }, TypeTLSA: func() RR { return new(TLSA) }, TypeHIP: func() RR { return new(HIP) }, TypeNID: func() RR { return new(NID) }, TypeL32: func() RR { return new(L32) }, TypeL64: func() RR { return new(L64) }, TypeLP: func() RR { return new(LP) }, TypeEUI48: func() RR { return new(EUI48) }, TypeEUI64: func() RR { return new(EUI64) }, TypeCAA: func() RR { return new(CAA) }, TypeUID: func() RR { return new(UID) }, TypeGID: func() RR { return new(GID) }, TypeUINFO: func() RR { return new(UINFO) }, } golang-dns-0.0~git20130912/update.go000066400000000000000000000113161221430110200167230ustar00rootroot00000000000000// 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. // 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 NameUsed // ANY rrset empty RRset exists (value indep) RRsetUsed // NONE ANY empty Name is not in use NameNotUsed // NONE rrset empty RRset does not exist RRsetNotUsed // zone rrset rr RRset exists (value dep) 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 RemoveName // ANY rrset empty Delete an RRset RemoveRRset // NONE rrset rr Delete an RR from RRset Remove // zone rrset rr Add to an RRset Insert // package dns // NameUsed sets the RRs in the prereq section to // "Name is in use" RRs. RFC 2136 section 2.4.4. func (u *Msg) NameUsed(rr []RR) { u.Answer = make([]RR, len(rr)) for i, r := range rr { u.Answer[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}} } } // NameNotUsed sets the RRs in the prereq section to // "Name is in not use" RRs. RFC 2136 section 2.4.5. func (u *Msg) NameNotUsed(rr []RR) { u.Answer = make([]RR, len(rr)) for i, r := range rr { u.Answer[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}} } } // Used sets the RRs in the prereq section to // "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2. func (u *Msg) Used(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") } u.Answer = make([]RR, len(rr)) for i, r := range rr { u.Answer[i] = r u.Answer[i].Header().Class = u.Question[0].Qclass } } // RRsetUsed sets the RRs in the prereq section to // "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1. func (u *Msg) RRsetUsed(rr []RR) { u.Answer = make([]RR, len(rr)) for i, r := range rr { u.Answer[i] = r u.Answer[i].Header().Class = ClassANY u.Answer[i].Header().Ttl = 0 u.Answer[i].Header().Rdlength = 0 } } // RRsetNotUsed sets the RRs in the prereq section to // "RRset does not exist" RRs. RFC 2136 section 2.4.3. func (u *Msg) RRsetNotUsed(rr []RR) { u.Answer = make([]RR, len(rr)) for i, r := range rr { u.Answer[i] = r u.Answer[i].Header().Class = ClassNONE u.Answer[i].Header().Rdlength = 0 u.Answer[i].Header().Ttl = 0 } } // Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1. func (u *Msg) Insert(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") } u.Ns = make([]RR, len(rr)) for i, r := range rr { u.Ns[i] = r u.Ns[i].Header().Class = u.Question[0].Qclass } } // RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2. func (u *Msg) RemoveRRset(rr []RR) { u.Ns = make([]RR, len(rr)) for i, r := range rr { u.Ns[i] = r u.Ns[i].Header().Class = ClassANY u.Ns[i].Header().Rdlength = 0 u.Ns[i].Header().Ttl = 0 } } // RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3 func (u *Msg) RemoveName(rr []RR) { u.Ns = make([]RR, len(rr)) for i, r := range rr { u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}} } } // Remove creates a dynamic update packet deletes RR from the RRSset, see RFC 2136 section 2.5.4 func (u *Msg) Remove(rr []RR) { u.Ns = make([]RR, len(rr)) for i, r := range rr { u.Ns[i] = r u.Ns[i].Header().Class = ClassNONE u.Ns[i].Header().Ttl = 0 } } golang-dns-0.0~git20130912/xfr.go000066400000000000000000000120661221430110200162430ustar00rootroot00000000000000// 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. package dns // Envelope is used when doing [IA]XFR with a remote server. type Envelope struct { RR []RR // The set of RRs in the answer section of the AXFR reply message. Error error // If something went wrong, this contains the error. } // TransferIn performs a [AI]XFR request (depends on the message's Qtype). It returns // a channel of *Envelope on which the replies from the server are sent. At the end of // the transfer the channel is closed. // The messages are TSIG checked if // needed, no other post-processing is performed. The caller must dissect the returned // messages. // // Basic use pattern for receiving an AXFR: // // // m contains the AXFR request // t, e := c.TransferIn(m, "127.0.0.1:53") // for r := range t { // // ... deal with r.RR or r.Error // } func (c *Client) TransferIn(q *Msg, a string) (chan *Envelope, error) { w := new(reply) w.client = c w.addr = a w.req = q if err := w.dial(); err != nil { return nil, err } if err := w.send(q); err != nil { return nil, err } e := make(chan *Envelope) switch q.Question[0].Qtype { case TypeAXFR: go w.axfrIn(q, e) return e, nil case TypeIXFR: go w.ixfrIn(q, e) return e, nil default: return nil, nil } panic("dns: not reached") } func (w *reply) axfrIn(q *Msg, c chan *Envelope) { first := true defer w.conn.Close() defer close(c) for { in, err := w.receive() if err != nil { c <- &Envelope{nil, err} return } if in.Id != q.Id { c <- &Envelope{in.Answer, ErrId} return } if first { if !checkXfrSOA(in, true) { c <- &Envelope{in.Answer, ErrSoa} return } first = !first // only one answer that is SOA, receive more if len(in.Answer) == 1 { w.tsigTimersOnly = true c <- &Envelope{in.Answer, nil} continue } } if !first { w.tsigTimersOnly = true // Subsequent envelopes use this. if checkXfrSOA(in, false) { c <- &Envelope{in.Answer, nil} return } c <- &Envelope{in.Answer, nil} } } panic("dns: not reached") } func (w *reply) ixfrIn(q *Msg, c chan *Envelope) { var serial uint32 // The first serial seen is the current server serial first := true defer w.conn.Close() defer close(c) for { in, err := w.receive() if err != nil { c <- &Envelope{in.Answer, err} return } if q.Id != in.Id { c <- &Envelope{in.Answer, ErrId} return } if first { // A single SOA RR signals "no changes" if len(in.Answer) == 1 && checkXfrSOA(in, true) { c <- &Envelope{in.Answer, nil} return } // Check if the returned answer is ok if !checkXfrSOA(in, true) { c <- &Envelope{in.Answer, ErrSoa} return } // This serial is important serial = in.Answer[0].(*SOA).Serial first = !first } // Now we need to check each message for SOA records, to see what we need to do if !first { w.tsigTimersOnly = true // If the last record in the IXFR contains the servers' SOA, we should quit if v, ok := in.Answer[len(in.Answer)-1].(*SOA); ok { if v.Serial == serial { c <- &Envelope{in.Answer, nil} return } } c <- &Envelope{in.Answer, nil} } } panic("dns: not reached") } // Check if he SOA record exists in the Answer section of // the packet. If first is true the first RR must be a SOA // if false, the last one should be a SOA. func checkXfrSOA(in *Msg, first bool) bool { if len(in.Answer) > 0 { if first { return in.Answer[0].Header().Rrtype == TypeSOA } else { return in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA } } return false } // TransferOut performs an outgoing [AI]XFR depending on the request message. The // caller is responsible for sending the correct sequence of RR sets through // the channel c. For reasons of symmetry Envelope is re-used. // Errors are signaled via the error pointer, when an error occurs the function // sets the error and returns (it does not close the channel). // TSIG and enveloping is handled by TransferOut. // // Basic use pattern for sending an AXFR: // // // q contains the AXFR request // c := make(chan *Envelope) // var e *error // err := TransferOut(w, q, c, e) // w.Hijack() // hijack the connection so that the package doesn't close it // for _, rrset := range rrsets { // rrsets is a []RR // c <- &{Envelope{RR: rrset} // if e != nil { // close(c) // break // } // } // // w.Close() // Don't! Let the client close the connection func TransferOut(w ResponseWriter, q *Msg, c chan *Envelope, e *error) error { switch q.Question[0].Qtype { case TypeAXFR, TypeIXFR: go xfrOut(w, q, c, e) return nil default: return nil } panic("dns: not reached") } // TODO(mg): count the RRs and the resulting size. func xfrOut(w ResponseWriter, req *Msg, c chan *Envelope, e *error) { rep := new(Msg) rep.SetReply(req) rep.Authoritative = true for x := range c { // assume it fits rep.Answer = append(rep.Answer, x.RR...) if err := w.WriteMsg(rep); e != nil { *e = err return } w.TsigTimersOnly(true) rep.Answer = nil } } golang-dns-0.0~git20130912/zgenerate.go000066400000000000000000000074401221430110200174300ustar00rootroot00000000000000// 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. package dns import ( "fmt" "strconv" "strings" ) // Parse the $GENERATE statement as used in BIND9 zones. // See http://www.zytrax.com/books/dns/ch8/generate.html for instance. // We are called after '$GENERATE '. After which we expect: // * the range (12-24/2) // * lhs (ownername) // * [[ttl][class]] // * type // * rhs (rdata) // But we are lazy here, only the range is parsed *all* occurences // of $ after that are interpreted. // Any error are returned as a string value, the empty string signals // "no error". func generate(l lex, c chan lex, t chan Token, o string) string { step := 1 if i := strings.IndexAny(l.token, "/"); i != -1 { if i+1 == len(l.token) { return "bad step in $GENERATE range" } if s, e := strconv.Atoi(l.token[i+1:]); e != nil { return "bad step in $GENERATE range" } else { if s < 0 { return "bad step in $GENERATE range" } step = s } 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 != _NEWLINE && l.value != _EOF { s += l.token goto BuildRR } for i := start; i <= end; i += step { var ( escape bool dom string mod string err string offset int ) for j := 0; j < len(s); j++ { // No 'range' because we need to jump around switch s[j] { case '\\': if escape { dom += "\\" escape = false continue } escape = true case '$': mod = "%d" offset = 0 if escape { dom += "$" escape = false continue } escape = false if j+1 >= len(s) { // End of the string dom += fmt.Sprintf(mod, i+offset) continue } else { if s[j+1] == '$' { dom += "$" j++ continue } } // Search for { and } if s[j+1] == '{' { // Modifier block sep := strings.Index(s[j+2:], "}") if sep == -1 { return "bad modifier in $GENERATE" } mod, offset, err = modToPrintf(s[j+2 : j+2+sep]) if err != "" { return err } j += 2 + sep // Jump to it } dom += fmt.Sprintf(mod, i+offset) default: if escape { // Pretty useless here escape = false continue } dom += string(s[j]) } } // Re-parse the RR and send it on the current channel t rx, e := NewRR("$ORIGIN " + o + "\n" + dom) if e != nil { return e.(*ParseError).err } t <- Token{RR: rx} // Its more efficient to first built the rrlist and then parse it in // one go! But is this a problem? } return "" } // Convert a $GENERATE modifier 0,0,d to something Printf can deal with. func modToPrintf(s string) (string, int, string) { xs := strings.SplitN(s, ",", 3) if len(xs) != 3 { return "", 0, "bad modifier in $GENERATE" } // xs[0] is offset, xs[1] is width, xs[2] is base if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" { return "", 0, "bad base in $GENERATE" } offset, err := strconv.Atoi(xs[0]) if err != nil { return "", 0, "bad offset in $GENERATE" } width, err := strconv.Atoi(xs[1]) if err != nil { return "", offset, "bad width in $GENERATE" } switch { case width < 0: return "", offset, "bad width in $GENERATE" case width == 0: return "%" + xs[1] + xs[2], offset, "" default: return "%0" + xs[1] + xs[2], offset, "" } panic("dns: not reached") } golang-dns-0.0~git20130912/zscan.go000066400000000000000000000547331221430110200165710ustar00rootroot00000000000000// 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. package dns import ( "io" "log" "os" "strconv" "strings" ) type debugging bool const debug debugging = false func (d debugging) Printf(format string, args ...interface{}) { if d { log.Printf(format, args...) } } const maxTok = 2048 // Largest token we can return. // Tokinize a RFC 1035 zone file. The tokenizer will normalize it: // * Add ownernames if they are left blank; // * Suppress sequences of spaces; // * Make each RR fit on one line (_NEWLINE is send as last) // * Handle comments: ; // * Handle braces - anywhere. const ( // Zonefile _EOF = iota _STRING _BLANK _QUOTE _NEWLINE _RRTYPE _OWNER _CLASS _DIRORIGIN // $ORIGIN _DIRTTL // $TTL _DIRINCLUDE // $INCLUDE _DIRGENERATE // $GENERATE // Privatekey file _VALUE _KEY _EXPECT_OWNER_DIR // Ownername _EXPECT_OWNER_BL // Whitespace after the ownername _EXPECT_ANY // Expect rrtype, ttl or class _EXPECT_ANY_NOCLASS // Expect rrtype or ttl _EXPECT_ANY_NOCLASS_BL // The whitespace after _EXPECT_ANY_NOCLASS _EXPECT_ANY_NOTTL // Expect rrtype or class _EXPECT_ANY_NOTTL_BL // Whitespace after _EXPECT_ANY_NOTTL _EXPECT_RRTYPE // Expect rrtype _EXPECT_RRTYPE_BL // Whitespace BEFORE rrtype _EXPECT_RDATA // The first element of the rdata _EXPECT_DIRTTL_BL // Space after directive $TTL _EXPECT_DIRTTL // Directive $TTL _EXPECT_DIRORIGIN_BL // Space after directive $ORIGIN _EXPECT_DIRORIGIN // Directive $ORIGIN _EXPECT_DIRINCLUDE_BL // Space after directive $INCLUDE _EXPECT_DIRINCLUDE // Directive $INCLUDE _EXPECT_DIRGENERATE // Directive $GENERATE _EXPECT_DIRGENERATE_BL // Space after directive $GENERATE ) // ParseError is a parsing error. It contains the parse error and the location in the io.Reader // where the error occured. type ParseError struct { file string err string lex lex } func (e *ParseError) Error() (s string) { if e.file != "" { s = e.file + ": " } s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " + strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column) return } type lex struct { token string // text of the token length int // lenght of the token err bool // when true, token text has lexer error value uint8 // value: _STRING, _BLANK, etc. line int // line in the file column int // column in the file torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar comment string // any comment text seen } // Tokens are returned when a zone file is parsed. type Token struct { RR // the scanned resource record when error is not nil Error *ParseError // when an error occured, this has the error specifics Comment string // A potential comment positioned after the RR and on the same line } // NewRR reads the RR contained in the string s. Only the first RR is returned. // The class defaults to IN and TTL defaults to 3600. The full zone file // syntax like $TTL, $ORIGIN, etc. is supported. func NewRR(s string) (RR, error) { if s[len(s)-1] != '\n' { // We need a closing newline return ReadRR(strings.NewReader(s+"\n"), "") } return ReadRR(strings.NewReader(s), "") } // ReadRR reads the RR contained in q. Only the first RR is returned. // The class defaults to IN and TTL defaults to 3600. func ReadRR(q io.Reader, filename string) (RR, error) { r := <-parseZoneHelper(q, ".", filename, 1) if r.Error != nil { return nil, r.Error } return r.RR, nil } // ParseZone reads a RFC 1035 style one from r. It returns Tokens on the // returned channel, which consist out the parsed RR, a potential comment or an error. // If there is an error the RR is nil. The string file is only used // in error reporting. The string origin is used as the initial origin, as // if the file would start with: $ORIGIN origin . // The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are supported. // The channel t is closed by ParseZone when the end of r is reached. // // Basic usage pattern when reading from a string (z) containing the // zone data: // // for x := range dns.ParseZone(strings.NewReader(z), "", "") { // if x.Error != nil { // // Do something with x.RR // } // } // // Comments specified after an RR (and on the same line!) are returned too: // // foo. IN A 10.0.0.1 ; this is a comment // // The text "; this is comment" is returned in Token.Comment . Comments inside the // RR are discarded. Comments on a line by themselves are discarded too. func ParseZone(r io.Reader, origin, file string) chan Token { return parseZoneHelper(r, origin, file, 10000) } func parseZoneHelper(r io.Reader, origin, file string, chansize int) chan Token { t := make(chan Token, chansize) go parseZone(r, origin, file, t, 0) return t } func parseZone(r io.Reader, origin, f string, t chan Token, include int) { defer func() { if include == 0 { close(t) } }() s := scanInit(r) c := make(chan lex, 1000) // Start the lexer go zlexer(s, c) // 6 possible beginnings of a line, _ is a space // 0. _RRTYPE -> all omitted until the rrtype // 1. _OWNER _ _RRTYPE -> class/ttl omitted // 2. _OWNER _ _STRING _ _RRTYPE -> class omitted // 3. _OWNER _ _STRING _ _CLASS _ _RRTYPE -> ttl/class // 4. _OWNER _ _CLASS _ _RRTYPE -> ttl omitted // 5. _OWNER _ _CLASS _ _STRING _ _RRTYPE -> class/ttl (reversed) // After detecting these, we know the _RRTYPE so we can jump to functions // handling the rdata for each of these types. if origin == "" { origin = "." } origin = Fqdn(origin) if _, ok := IsDomainName(origin); !ok { t <- Token{Error: &ParseError{f, "bad initial origin name", lex{}}} return } st := _EXPECT_OWNER_DIR // initial state var h RR_Header var defttl uint32 = defaultTtl var prevName string for l := range c { // Lexer spotted an error already if l.err == true { t <- Token{Error: &ParseError{f, l.token, l}} return } switch st { case _EXPECT_OWNER_DIR: // We can also expect a directive, like $TTL or $ORIGIN h.Ttl = defttl h.Class = ClassINET switch l.value { case _NEWLINE: // Empty line st = _EXPECT_OWNER_DIR case _OWNER: h.Name = l.token if l.token[0] == '@' { h.Name = origin prevName = h.Name st = _EXPECT_OWNER_BL break } if h.Name[l.length-1] != '.' { h.Name = appendOrigin(h.Name, origin) } _, ok := IsDomainName(l.token) if !ok { t <- Token{Error: &ParseError{f, "bad owner name", l}} return } prevName = h.Name st = _EXPECT_OWNER_BL case _DIRTTL: st = _EXPECT_DIRTTL_BL case _DIRORIGIN: st = _EXPECT_DIRORIGIN_BL case _DIRINCLUDE: st = _EXPECT_DIRINCLUDE_BL case _DIRGENERATE: st = _EXPECT_DIRGENERATE_BL case _RRTYPE: // Everthing has been omitted, this is the first thing on the line h.Name = prevName h.Rrtype = l.torc st = _EXPECT_RDATA case _CLASS: // First thing on the line is the class h.Name = prevName h.Class = l.torc st = _EXPECT_ANY_NOCLASS_BL case _BLANK: // Discard, can happen when there is nothing on the // line except the RR type case _STRING: // First thing on the is the ttl if ttl, ok := stringToTtl(l.token); !ok { t <- Token{Error: &ParseError{f, "not a TTL", l}} return } else { h.Ttl = ttl // Don't about the defttl, we should take the $TTL value // defttl = ttl } st = _EXPECT_ANY_NOTTL_BL default: t <- Token{Error: &ParseError{f, "syntax error at beginning", l}} return } case _EXPECT_DIRINCLUDE_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}} return } st = _EXPECT_DIRINCLUDE case _EXPECT_DIRINCLUDE: if l.value != _STRING { t <- Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}} return } neworigin := origin // There may be optionally a new origin set after the filename, if not use current one l := <-c switch l.value { case _BLANK: l := <-c if l.value == _STRING { if _, ok := IsDomainName(l.token); !ok { t <- Token{Error: &ParseError{f, "bad origin name", l}} return } // a new origin is specified. if l.token[l.length-1] != '.' { if origin != "." { // Prevent .. endings neworigin = l.token + "." + origin } else { neworigin = l.token + origin } } else { neworigin = l.token } } case _NEWLINE, _EOF: // Ok default: t <- Token{Error: &ParseError{f, "garbage after $INCLUDE", l}} return } // Start with the new file r1, e1 := os.Open(l.token) if e1 != nil { t <- Token{Error: &ParseError{f, "failed to open `" + l.token + "'", l}} return } if include+1 > 7 { t <- Token{Error: &ParseError{f, "too deeply nested $INCLUDE", l}} return } parseZone(r1, l.token, neworigin, t, include+1) st = _EXPECT_OWNER_DIR case _EXPECT_DIRTTL_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank after $TTL-directive", l}} return } st = _EXPECT_DIRTTL case _EXPECT_DIRTTL: if l.value != _STRING { t <- Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} return } if e, _ := slurpRemainder(c, f); e != nil { t <- Token{Error: e} return } if ttl, ok := stringToTtl(l.token); !ok { t <- Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} return } else { defttl = ttl } st = _EXPECT_OWNER_DIR case _EXPECT_DIRORIGIN_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}} return } st = _EXPECT_DIRORIGIN case _EXPECT_DIRORIGIN: if l.value != _STRING { t <- Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}} return } if e, _ := slurpRemainder(c, f); e != nil { t <- Token{Error: e} } if _, ok := IsDomainName(l.token); !ok { t <- Token{Error: &ParseError{f, "bad origin name", l}} return } if l.token[l.length-1] != '.' { if origin != "." { // Prevent .. endings origin = l.token + "." + origin } else { origin = l.token + origin } } else { origin = l.token } st = _EXPECT_OWNER_DIR case _EXPECT_DIRGENERATE_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}} return } st = _EXPECT_DIRGENERATE case _EXPECT_DIRGENERATE: if l.value != _STRING { t <- Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}} return } if e := generate(l, c, t, origin); e != "" { t <- Token{Error: &ParseError{f, e, l}} return } st = _EXPECT_OWNER_DIR case _EXPECT_OWNER_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank after owner", l}} return } st = _EXPECT_ANY case _EXPECT_ANY: switch l.value { case _RRTYPE: h.Rrtype = l.torc st = _EXPECT_RDATA case _CLASS: h.Class = l.torc st = _EXPECT_ANY_NOCLASS_BL case _STRING: // TTL is this case if ttl, ok := stringToTtl(l.token); !ok { t <- Token{Error: &ParseError{f, "not a TTL", l}} return } else { h.Ttl = ttl // defttl = ttl // don't set the defttl here } st = _EXPECT_ANY_NOTTL_BL default: t <- Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}} return } case _EXPECT_ANY_NOCLASS_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank before class", l}} return } st = _EXPECT_ANY_NOCLASS case _EXPECT_ANY_NOTTL_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank before TTL", l}} return } st = _EXPECT_ANY_NOTTL case _EXPECT_ANY_NOTTL: switch l.value { case _CLASS: h.Class = l.torc st = _EXPECT_RRTYPE_BL case _RRTYPE: h.Rrtype = l.torc st = _EXPECT_RDATA default: t <- Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}} return } case _EXPECT_ANY_NOCLASS: switch l.value { case _STRING: // TTL if ttl, ok := stringToTtl(l.token); !ok { t <- Token{Error: &ParseError{f, "not a TTL", l}} return } else { h.Ttl = ttl // defttl = ttl // don't set the def ttl anymore } st = _EXPECT_RRTYPE_BL case _RRTYPE: h.Rrtype = l.torc st = _EXPECT_RDATA default: t <- Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}} return } case _EXPECT_RRTYPE_BL: if l.value != _BLANK { t <- Token{Error: &ParseError{f, "no blank before RR type", l}} return } st = _EXPECT_RRTYPE case _EXPECT_RRTYPE: if l.value != _RRTYPE { t <- Token{Error: &ParseError{f, "unknown RR type", l}} return } h.Rrtype = l.torc st = _EXPECT_RDATA case _EXPECT_RDATA: r, e, c1 := setRR(h, c, origin, f) if e != nil { // If e.lex is nil than we have encounter a unknown RR type // in that case we substitute our current lex token if e.lex.token == "" && e.lex.value == 0 { e.lex = l // Uh, dirty } t <- Token{Error: e} return } t <- Token{RR: r, Comment: c1} st = _EXPECT_OWNER_DIR } } // If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this // is not an error, because an empty zone file is still a zone file. } // zlexer scans the sourcefile and returns tokens on the channel c. func zlexer(s *scan, c chan lex) { var l lex str := make([]byte, maxTok) // Should be enough for any token stri := 0 // Offset in str (0 means empty) com := make([]byte, maxTok) // Hold comment text comi := 0 quote := false escape := false space := false commt := false rrtype := false owner := true brace := 0 x, err := s.tokenText() defer close(c) for err == nil { l.column = s.position.Column l.line = s.position.Line if stri > maxTok { l.token = "tok length insufficient for parsing" l.err = true debug.Printf("[%+v]", l.token) c <- l return } if comi > maxTok { l.token = "com length insufficient for parsing" l.err = true debug.Printf("[%+v]", l.token) c <- l return } switch x { case ' ', '\t': if quote { // Inside quotes this is legal str[stri] = x stri++ break } escape = false if commt { com[comi] = x comi++ break } if stri == 0 { // Space directly in the beginning, handled in the grammar } else if owner { // If we have a string and its the first, make it an owner l.value = _OWNER l.token = string(str[:stri]) l.length = stri // escape $... start with a \ not a $, so this will work switch l.token { case "$TTL": l.value = _DIRTTL case "$ORIGIN": l.value = _DIRORIGIN case "$INCLUDE": l.value = _DIRINCLUDE case "$GENERATE": l.value = _DIRGENERATE } debug.Printf("[7 %+v]", l.token) c <- l } else { l.value = _STRING l.token = string(str[:stri]) l.length = stri if !rrtype { if t, ok := StringToType[l.token]; ok { l.value = _RRTYPE l.torc = t rrtype = true } else { if strings.HasPrefix(l.token, "TYPE") { if t, ok := typeToInt(l.token); !ok { l.token = "unknown RR type" l.err = true // no lexer debug c <- l return } else { l.value = _RRTYPE l.torc = t } } } if t, ok := StringToClass[l.token]; ok { l.value = _CLASS l.torc = t } else { if strings.HasPrefix(l.token, "CLASS") { if t, ok := classToInt(l.token); !ok { l.token = "unknown class" l.err = true // no lexer debug c <- l return } else { l.value = _CLASS l.torc = t } } } } debug.Printf("[6 %+v]", l.token) c <- l } stri = 0 // I reverse space stuff here if !space && !commt { l.value = _BLANK l.token = " " l.length = 1 debug.Printf("[5 %+v]", l.token) c <- l } owner = false space = true case ';': if quote { // Inside quotes this is legal str[stri] = x stri++ break } if escape { escape = false str[stri] = x stri++ break } if stri > 0 { l.value = _STRING l.token = string(str[:stri]) l.length = stri debug.Printf("[4 %+v]", l.token) c <- l stri = 0 } commt = true com[comi] = ';' comi++ case '\r': // discard // this means it can also not be used as rdata case '\n': // Escaped newline if quote { str[stri] = x stri++ break } // inside quotes this is legal escape = false if commt { // Reset a comment commt = false rrtype = false stri = 0 // If not in a brace this ends the comment AND the RR if brace == 0 { owner = true owner = true l.value = _NEWLINE l.token = "\n" l.length = 1 l.comment = string(com[:comi]) debug.Printf("[3 %+v %+v]", l.token, l.comment) c <- l l.comment = "" comi = 0 break } com[comi] = ' ' // convert newline to space comi++ break } if brace == 0 { // If there is previous text, we should output it here if stri != 0 { l.value = _STRING l.token = string(str[:stri]) l.length = stri if !rrtype { if t, ok := StringToType[strings.ToUpper(l.token)]; ok { l.value = _RRTYPE l.torc = t rrtype = true } } debug.Printf("[2 %+v]", l.token) c <- l } l.value = _NEWLINE l.token = "\n" l.length = 1 debug.Printf("[1 %+v]", l.token) c <- l stri = 0 commt = false rrtype = false owner = true comi = 0 } case '\\': // quote? if commt { com[comi] = x comi++ break } if escape { str[stri] = x stri++ escape = false break } str[stri] = x stri++ escape = true case '"': if commt { com[comi] = x comi++ break } if escape { str[stri] = x stri++ escape = false break } space = false // send previous gathered text and the quote if stri != 0 { l.value = _STRING l.token = string(str[:stri]) l.length = stri debug.Printf("[%+v]", l.token) c <- l stri = 0 } l.value = _QUOTE l.token = "\"" l.length = 1 c <- l quote = !quote case '(', ')': if quote { str[stri] = x stri++ break } if commt { com[comi] = x comi++ break } if escape { str[stri] = x stri++ escape = false break } switch x { case ')': brace-- if brace < 0 { l.token = "extra closing brace" l.err = true debug.Printf("[%+v]", l.token) c <- l return } case '(': brace++ } default: if commt { com[comi] = x comi++ break } escape = false str[stri] = x stri++ space = false } x, err = s.tokenText() } if stri > 0 { // Send remainder l.token = string(str[:stri]) l.length = stri l.value = _STRING debug.Printf("[%+v]", l.token) c <- l } } // Extract the class number from CLASSxx func classToInt(token string) (uint16, bool) { class, ok := strconv.Atoi(token[5:]) if ok != nil { return 0, false } return uint16(class), true } // Extract the rr number from TYPExxx func typeToInt(token string) (uint16, bool) { typ, ok := strconv.Atoi(token[4:]) if ok != nil { return 0, false } return uint16(typ), true } // Parse things like 2w, 2m, etc, Return the time in seconds. func stringToTtl(token string) (uint32, bool) { s := uint32(0) i := uint32(0) for _, c := range token { switch c { case 's', 'S': s += i i = 0 case 'm', 'M': s += i * 60 i = 0 case 'h', 'H': s += i * 60 * 60 i = 0 case 'd', 'D': s += i * 60 * 60 * 24 i = 0 case 'w', 'W': s += i * 60 * 60 * 24 * 7 i = 0 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': i *= 10 i += uint32(c) - '0' default: return 0, false } } return s + i, true } // Parse LOC records' [.][mM] into a // mantissa exponent format. Token should contain the entire // string (i.e. no spaces allowed) func stringToCm(token string) (e, m uint8, ok bool) { if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' { token = token[0 : len(token)-1] } s := strings.SplitN(token, ".", 2) var meters, cmeters, val int var err error switch len(s) { case 2: if cmeters, err = strconv.Atoi(s[1]); err != nil { return } fallthrough case 1: if meters, err = strconv.Atoi(s[0]); err != nil { return } case 0: // huh? return 0, 0, false } ok = true if meters > 0 { e = 2 val = meters } else { e = 0 val = cmeters } for val > 10 { e++ val /= 10 } if e > 9 { ok = false } m = uint8(val) return } func appendOrigin(name, origin string) string { if origin == "." { return name + origin } return name + "." + origin } // LOC record helper function func locCheckNorth(token string, latitude uint32) (uint32, bool) { switch token { case "n", "N": return _LOC_EQUATOR + latitude, true case "s", "S": return _LOC_EQUATOR - latitude, true } return latitude, false } // LOC record helper function func locCheckEast(token string, longitude uint32) (uint32, bool) { switch token { case "e", "E": return _LOC_EQUATOR + longitude, true case "w", "W": return _LOC_EQUATOR - longitude, true } return longitude, false } // "Eat" the rest of the "line". Return potential comments func slurpRemainder(c chan lex, f string) (*ParseError, string) { l := <-c com := "" switch l.value { case _BLANK: l = <-c com = l.comment if l.value != _NEWLINE && l.value != _EOF { return &ParseError{f, "garbage after rdata", l}, "" } case _NEWLINE: com = l.comment case _EOF: default: return &ParseError{f, "garbage after rdata", l}, "" } return nil, com } // Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64" // Used for NID and L64 record. func stringToNodeID(l lex) (uint64, *ParseError) { if len(l.token) < 19 { return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} } // There must be three colons at fixes postitions, if not its a parse error if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} } s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19] u, e := strconv.ParseUint(s, 16, 64) if e != nil { return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} } return u, nil } golang-dns-0.0~git20130912/zscan_rr.go000066400000000000000000001211201221430110200172550ustar00rootroot00000000000000// 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. package dns import ( "encoding/base64" "net" "strconv" "strings" ) // Parse the rdata of each rrtype. // All data from the channel c is either _STRING or _BLANK. // After the rdata there may come a _BLANK and then a _NEWLINE // or immediately a _NEWLINE. If this is not the case we flag // an *ParseError: garbage after rdata. func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { var r RR e := new(ParseError) switch h.Rrtype { case TypeA: r, e = setA(h, c, f) goto Slurp case TypeAAAA: r, e = setAAAA(h, c, f) goto Slurp case TypeHINFO: r, e = setHINFO(h, c, f) goto Slurp case TypeMINFO: r, e = setMINFO(h, c, o, f) goto Slurp case TypeNS: r, e = setNS(h, c, o, f) goto Slurp case TypePTR: r, e = setPTR(h, c, o, f) goto Slurp case TypeMF: r, e = setMF(h, c, o, f) goto Slurp case TypeMD: r, e = setMD(h, c, o, f) goto Slurp case TypeMG: r, e = setMG(h, c, o, f) goto Slurp case TypeRT: r, e = setRT(h, c, o, f) goto Slurp case TypeAFSDB: r, e = setAFSDB(h, c, o, f) goto Slurp case TypeX25: r, e = setX25(h, c, f) goto Slurp case TypeMX: r, e = setMX(h, c, o, f) goto Slurp case TypeCNAME: r, e = setCNAME(h, c, o, f) goto Slurp case TypeDNAME: r, e = setDNAME(h, c, o, f) goto Slurp case TypeSOA: r, e = setSOA(h, c, o, f) goto Slurp case TypeSSHFP: r, e = setSSHFP(h, c, f) goto Slurp case TypeSRV: r, e = setSRV(h, c, o, f) goto Slurp case TypeNAPTR: r, e = setNAPTR(h, c, o, f) goto Slurp case TypeTALINK: r, e = setTALINK(h, c, o, f) goto Slurp case TypeRP: r, e = setRP(h, c, o, f) goto Slurp case TypeMR: r, e = setMR(h, c, o, f) goto Slurp case TypeMB: r, e = setMB(h, c, o, f) goto Slurp case TypeKX: r, e = setKX(h, c, o, f) goto Slurp case TypeNID: r, e = setNID(h, c, f) goto Slurp case TypeL32: r, e = setL32(h, c, f) goto Slurp case TypeL64: r, e = setL64(h, c, f) goto Slurp case TypeLP: r, e = setLP(h, c, o, f) goto Slurp case TypeNSEC3PARAM: r, e = setNSEC3PARAM(h, c, f) goto Slurp case TypeEUI48: r, e = setEUI48(h, c, f) goto Slurp case TypeEUI64: r, e = setEUI64(h, c, f) goto Slurp case TypeUID: r, e = setUID(h, c, f) goto Slurp case TypeGID: r, e = setGID(h, c, f) goto Slurp case TypeLOC: r, e = setLOC(h, c, f) goto Slurp // These types have a variable ending: either chunks of txt or chunks/base64 or hex. // They need to search for the end of the RR themselves, hence they look for the ending // newline. Thus there is no need to slurp the remainder, because there is none. case TypeDNSKEY: return setDNSKEY(h, c, f) case TypeRKEY: return setRKEY(h, c, f) case TypeRRSIG: return setRRSIG(h, c, o, f) case TypeNSEC: return setNSEC(h, c, o, f) case TypeNSEC3: return setNSEC3(h, c, o, f) case TypeWKS: return setWKS(h, c, f) case TypeDS: return setDS(h, c, f) case TypeCDS: return setCDS(h, c, f) case TypeDLV: return setDLV(h, c, f) case TypeTA: return setTA(h, c, f) case TypeTLSA: return setTLSA(h, c, f) case TypeTXT: return setTXT(h, c, f) case TypeURI: return setURI(h, c, f) case TypeNINFO: return setNINFO(h, c, f) case TypeHIP: return setHIP(h, c, o, f) case TypeSPF: return setSPF(h, c, f) case TypeDHCID: return setDHCID(h, c, f) case TypeIPSECKEY: return setIPSECKEY(h, c, o, f) case TypeUINFO: return setUINFO(h, c, f) case TypeCERT: return setCERT(h, c, f) default: // RFC3957 RR (Unknown RR handling) return setRFC3597(h, c, f) } Slurp: if e != nil { return nil, e, "" } se, com := slurpRemainder(c, f) if se != nil { return nil, se, "" } return r, e, com } // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error func endingToString(c chan lex, errstr, f string) (string, *ParseError, string) { s := "" l := <-c // _STRING for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _STRING: s += l.token case _BLANK: // Ok default: return "", &ParseError{f, errstr, l}, "" } l = <-c } return s, nil, l.comment } // A remainder of the rdata with embedded spaces, return the parsed string slice (sans the spaces) // or an error func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, string) { // Get the remaining data until we see a NEWLINE quote := false l := <-c var s []string switch l.value == _QUOTE { case true: // A number of quoted string s = make([]string, 0) for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _STRING: s = append(s, l.token) case _BLANK: if quote { // _BLANK can only be seen in between txt parts. return nil, &ParseError{f, errstr, l}, "" } case _QUOTE: quote = !quote default: return nil, &ParseError{f, errstr, l}, "" } l = <-c } if quote { return nil, &ParseError{f, errstr, l}, "" } case false: // Unquoted text record s = make([]string, 1) for l.value != _NEWLINE && l.value != _EOF { s[0] += l.token l = <-c } } return s, nil, l.comment } func setA(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(A) rr.Hdr = h l := <-c rr.A = net.ParseIP(l.token) if rr.A == nil { return nil, &ParseError{f, "bad A A", l} } return rr, nil } func setAAAA(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(AAAA) rr.Hdr = h l := <-c rr.AAAA = net.ParseIP(l.token) if rr.AAAA == nil { return nil, &ParseError{f, "bad AAAA AAAA", l} } return rr, nil } func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(NS) rr.Hdr = h l := <-c rr.Ns = l.token if l.token == "@" { rr.Ns = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad NS Ns", l} } if rr.Ns[l.length-1] != '.' { rr.Ns = appendOrigin(rr.Ns, o) } return rr, nil } func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(PTR) rr.Hdr = h l := <-c rr.Ptr = l.token if l.token == "@" { rr.Ptr = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad PTR Ptr", l} } if rr.Ptr[l.length-1] != '.' { rr.Ptr = appendOrigin(rr.Ptr, o) } return rr, nil } func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(RP) rr.Hdr = h l := <-c rr.Mbox = l.token if l.token == "@" { rr.Mbox = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RP Mbox", l} } if rr.Mbox[l.length-1] != '.' { rr.Mbox = appendOrigin(rr.Mbox, o) } } <-c // _BLANK l = <-c rr.Txt = l.token if l.token == "@" { rr.Txt = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RP Txt", l} } if rr.Txt[l.length-1] != '.' { rr.Txt = appendOrigin(rr.Txt, o) } return rr, nil } func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MR) rr.Hdr = h l := <-c rr.Mr = l.token if l.token == "@" { rr.Mr = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MR Mr", l} } if rr.Mr[l.length-1] != '.' { rr.Mr = appendOrigin(rr.Mr, o) } return rr, nil } func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MB) rr.Hdr = h l := <-c rr.Mb = l.token if l.token == "@" { rr.Mb = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MB Mb", l} } if rr.Mb[l.length-1] != '.' { rr.Mb = appendOrigin(rr.Mb, o) } return rr, nil } func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MG) rr.Hdr = h l := <-c rr.Mg = l.token if l.token == "@" { rr.Mg = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MG Mg", l} } if rr.Mg[l.length-1] != '.' { rr.Mg = appendOrigin(rr.Mg, o) } return rr, nil } func setHINFO(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(HINFO) rr.Hdr = h l := <-c rr.Cpu = l.token <-c // _BLANK l = <-c // _STRING rr.Os = l.token return rr, nil } func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MINFO) rr.Hdr = h l := <-c rr.Rmail = l.token if l.token == "@" { rr.Rmail = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MINFO Rmail", l} } if rr.Rmail[l.length-1] != '.' { rr.Rmail = appendOrigin(rr.Rmail, o) } } <-c // _BLANK l = <-c rr.Email = l.token if l.token == "@" { rr.Email = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MINFO Email", l} } if rr.Email[l.length-1] != '.' { rr.Email = appendOrigin(rr.Email, o) } return rr, nil } func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MF) rr.Hdr = h l := <-c rr.Mf = l.token if l.token == "@" { rr.Mf = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MF Mf", l} } if rr.Mf[l.length-1] != '.' { rr.Mf = appendOrigin(rr.Mf, o) } return rr, nil } func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MD) rr.Hdr = h l := <-c rr.Md = l.token if l.token == "@" { rr.Md = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MD Md", l} } if rr.Md[l.length-1] != '.' { rr.Md = appendOrigin(rr.Md, o) } return rr, nil } func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(MX) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad MX Pref", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Mx = l.token if l.token == "@" { rr.Mx = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad MX Mx", l} } if rr.Mx[l.length-1] != '.' { rr.Mx = appendOrigin(rr.Mx, o) } return rr, nil } func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(RT) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad RT Preference", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Host = l.token if l.token == "@" { rr.Host = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RT Host", l} } if rr.Host[l.length-1] != '.' { rr.Host = appendOrigin(rr.Host, o) } return rr, nil } func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(AFSDB) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad AFSDB Subtype", l} } else { rr.Subtype = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Hostname = l.token if l.token == "@" { rr.Hostname = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad AFSDB Hostname", l} } if rr.Hostname[l.length-1] != '.' { rr.Hostname = appendOrigin(rr.Hostname, o) } return rr, nil } func setX25(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(X25) rr.Hdr = h l := <-c rr.PSDNAddress = l.token return rr, nil } func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(KX) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad KX Pref", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Exchanger = l.token if l.token == "@" { rr.Exchanger = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad KX Exchanger", l} } if rr.Exchanger[l.length-1] != '.' { rr.Exchanger = appendOrigin(rr.Exchanger, o) } return rr, nil } func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(CNAME) rr.Hdr = h l := <-c rr.Target = l.token if l.token == "@" { rr.Target = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad CNAME Target", l} } if rr.Target[l.length-1] != '.' { rr.Target = appendOrigin(rr.Target, o) } return rr, nil } func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(DNAME) rr.Hdr = h l := <-c rr.Target = l.token if l.token == "@" { rr.Target = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad CNAME Target", l} } if rr.Target[l.length-1] != '.' { rr.Target = appendOrigin(rr.Target, o) } return rr, nil } func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(SOA) rr.Hdr = h l := <-c rr.Ns = l.token <-c // _BLANK if l.token == "@" { rr.Ns = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad SOA Ns", l} } if rr.Ns[l.length-1] != '.' { rr.Ns = appendOrigin(rr.Ns, o) } } l = <-c rr.Mbox = l.token if l.token == "@" { rr.Mbox = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad SOA Mbox", l} } if rr.Mbox[l.length-1] != '.' { rr.Mbox = appendOrigin(rr.Mbox, o) } } <-c // _BLANK var ( v uint32 ok bool ) for i := 0; i < 5; i++ { l = <-c if j, e := strconv.Atoi(l.token); e != nil { if i == 0 { // Serial should be a number return nil, &ParseError{f, "bad SOA zone parameter", l} } if v, ok = stringToTtl(l.token); !ok { return nil, &ParseError{f, "bad SOA zone parameter", l} } } else { v = uint32(j) } switch i { case 0: rr.Serial = v <-c // _BLANK case 1: rr.Refresh = v <-c // _BLANK case 2: rr.Retry = v <-c // _BLANK case 3: rr.Expire = v <-c // _BLANK case 4: rr.Minttl = v } } return rr, nil } func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(SRV) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad SRV Priority", l} } else { rr.Priority = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad SRV Weight", l} } else { rr.Weight = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad SRV Port", l} } else { rr.Port = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Target = l.token if l.token == "@" { rr.Target = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad SRV Target", l} } if rr.Target[l.length-1] != '.' { rr.Target = appendOrigin(rr.Target, o) } return rr, nil } func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(NAPTR) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NAPTR Order", l} } else { rr.Order = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NAPTR Preference", l} } else { rr.Preference = uint16(i) } // Flags <-c // _BLANK l = <-c // _QUOTE if l.value != _QUOTE { return nil, &ParseError{f, "bad NAPTR Flags", l} } l = <-c // Either String or Quote if l.value == _STRING { rr.Flags = l.token l = <-c // _QUOTE if l.value != _QUOTE { return nil, &ParseError{f, "bad NAPTR Flags", l} } } else if l.value == _QUOTE { rr.Flags = "" } else { return nil, &ParseError{f, "bad NAPTR Flags", l} } // Service <-c // _BLANK l = <-c // _QUOTE if l.value != _QUOTE { return nil, &ParseError{f, "bad NAPTR Service", l} } l = <-c // Either String or Quote if l.value == _STRING { rr.Service = l.token l = <-c // _QUOTE if l.value != _QUOTE { return nil, &ParseError{f, "bad NAPTR Service", l} } } else if l.value == _QUOTE { rr.Service = "" } else { return nil, &ParseError{f, "bad NAPTR Service", l} } // Regexp <-c // _BLANK l = <-c // _QUOTE if l.value != _QUOTE { return nil, &ParseError{f, "bad NAPTR Regexp", l} } l = <-c // Either String or Quote if l.value == _STRING { rr.Regexp = l.token l = <-c // _QUOTE if l.value != _QUOTE { return nil, &ParseError{f, "bad NAPTR Regexp", l} } } else if l.value == _QUOTE { rr.Regexp = "" } else { return nil, &ParseError{f, "bad NAPTR Regexp", l} } // After quote no space?? <-c // _BLANK l = <-c // _STRING rr.Replacement = l.token if l.token == "@" { rr.Replacement = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad NAPTR Replacement", l} } if rr.Replacement[l.length-1] != '.' { rr.Replacement = appendOrigin(rr.Replacement, o) } return rr, nil } func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(TALINK) rr.Hdr = h l := <-c rr.PreviousName = l.token if l.token == "@" { rr.PreviousName = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad TALINK PreviousName", l} } if rr.PreviousName[l.length-1] != '.' { rr.PreviousName = appendOrigin(rr.PreviousName, o) } } <-c // _BLANK l = <-c rr.NextName = l.token if l.token == "@" { rr.NextName = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad TALINK NextName", l} } if rr.NextName[l.length-1] != '.' { rr.NextName = appendOrigin(rr.NextName, o) } return rr, nil } func setLOC(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(LOC) rr.Hdr = h // Non zero defaults for LOC record, see RFC 1876, Section 3. rr.HorizPre = 165 // 10000 rr.VertPre = 162 // 10 rr.Size = 18 // 1 ok := false // North l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad LOC Latitude", l} } else { rr.Latitude = 1000 * 60 * 60 * uint32(i) } <-c // _BLANK // Either number, 'N' or 'S' l = <-c if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad LOC Latitude minutes", l} } else { rr.Latitude += 1000 * 60 * uint32(i) } <-c // _BLANK l = <-c if i, e := strconv.ParseFloat(l.token, 32); e != nil { return nil, &ParseError{f, "bad LOC Latitude seconds", l} } else { rr.Latitude += uint32(1000 * i) } <-c // _BLANK // Either number, 'N' or 'S' l = <-c if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } // If still alive, flag an error return nil, &ParseError{f, "bad LOC Latitude North/South", l} East: // East <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad LOC Longitude", l} } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) } <-c // _BLANK // Either number, 'E' or 'W' l = <-c if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad LOC Longitude minutes", l} } else { rr.Longitude += 1000 * 60 * uint32(i) } <-c // _BLANK l = <-c if i, e := strconv.ParseFloat(l.token, 32); e != nil { return nil, &ParseError{f, "bad LOC Longitude seconds", l} } else { rr.Longitude += uint32(1000 * i) } <-c // _BLANK // Either number, 'E' or 'W' l = <-c if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } // If still alive, flag an error return nil, &ParseError{f, "bad LOC Longitude East/West", l} Altitude: <-c // _BLANK l = <-c if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { l.token = l.token[0 : len(l.token)-1] } if i, e := strconv.ParseFloat(l.token, 32); e != nil { return nil, &ParseError{f, "bad LOC Altitude", l} } else { rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5) } // And now optionally the other values l = <-c count := 0 for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _STRING: switch count { case 0: // Size if e, m, ok := stringToCm(l.token); !ok { return nil, &ParseError{f, "bad LOC Size", l} } else { rr.Size = (e & 0x0f) | (m << 4 & 0xf0) } case 1: // HorizPre if e, m, ok := stringToCm(l.token); !ok { return nil, &ParseError{f, "bad LOC HorizPre", l} } else { rr.HorizPre = (e & 0x0f) | (m << 4 & 0xf0) } case 2: // VertPre if e, m, ok := stringToCm(l.token); !ok { return nil, &ParseError{f, "bad LOC VertPre", l} } else { rr.VertPre = (e & 0x0f) | (m << 4 & 0xf0) } } count++ case _BLANK: // Ok default: return nil, &ParseError{f, "bad LOC Size, HorizPre or VertPre", l} } l = <-c } return rr, nil } func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(HIP) rr.Hdr = h // HitLength is not represented l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad HIP PublicKeyAlgorithm", l}, "" } else { rr.PublicKeyAlgorithm = uint8(i) } <-c // _BLANK l = <-c // _STRING rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.HitLength = uint8(len(rr.Hit)) / 2 <-c // _BLANK l = <-c // _STRING rr.PublicKey = l.token // This cannot contain spaces rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey))) // RendezvousServers (if any) l = <-c xs := make([]string, 0) for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _STRING: if l.token == "@" { xs = append(xs, o) continue } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" } if l.token[l.length-1] != '.' { l.token = appendOrigin(l.token, o) } xs = append(xs, l.token) case _BLANK: // Ok default: return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" } l = <-c } rr.RendezvousServers = xs return rr, nil, l.comment } func setCERT(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(CERT) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad CERT Type", l}, "" } else { rr.Type = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad CERT KeyTag", l}, "" } else { rr.KeyTag = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad CERT Algorithm", l}, "" } else { rr.Algorithm = uint8(i) } s, e, c1 := endingToString(c, "bad CERT Certificate", f) if e != nil { return nil, e, c1 } rr.Certificate = s return rr, nil, c1 } func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(RRSIG) rr.Hdr = h l := <-c if t, ok := StringToType[strings.ToUpper(l.token)]; !ok { return nil, &ParseError{f, "bad RRSIG Typecovered", l}, "" } else { rr.TypeCovered = t } <-c // _BLANK l = <-c if i, err := strconv.Atoi(l.token); err != nil { return nil, &ParseError{f, "bad RRSIG Algorithm", l}, "" } else { rr.Algorithm = uint8(i) } <-c // _BLANK l = <-c if i, err := strconv.Atoi(l.token); err != nil { return nil, &ParseError{f, "bad RRSIG Labels", l}, "" } else { rr.Labels = uint8(i) } <-c // _BLANK l = <-c if i, err := strconv.Atoi(l.token); err != nil { return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, "" } else { rr.OrigTtl = uint32(i) } <-c // _BLANK l = <-c if i, err := StringToTime(l.token); err != nil { return nil, &ParseError{f, "bad RRSIG Expiration", l}, "" } else { rr.Expiration = i } <-c // _BLANK l = <-c if i, err := StringToTime(l.token); err != nil { return nil, &ParseError{f, "bad RRSIG Inception", l}, "" } else { rr.Inception = i } <-c // _BLANK l = <-c if i, err := strconv.Atoi(l.token); err != nil { return nil, &ParseError{f, "bad RRSIG KeyTag", l}, "" } else { rr.KeyTag = uint16(i) } <-c // _BLANK l = <-c rr.SignerName = l.token if l.token == "@" { rr.SignerName = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad RRSIG SignerName", l}, "" } if rr.SignerName[l.length-1] != '.' { rr.SignerName = appendOrigin(rr.SignerName, o) } } s, e, c1 := endingToString(c, "bad RRSIG Signature", f) if e != nil { return nil, e, c1 } rr.Signature = s return rr, nil, c1 } func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(NSEC) rr.Hdr = h l := <-c rr.NextDomain = l.token if l.token == "@" { rr.NextDomain = o } else { _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad NSEC NextDomain", l}, "" } if rr.NextDomain[l.length-1] != '.' { rr.NextDomain = appendOrigin(rr.NextDomain, o) } } rr.TypeBitMap = make([]uint16, 0) var ( k uint16 ok bool ) l = <-c for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _BLANK: // Ok case _STRING: if k, ok = StringToType[strings.ToUpper(l.token)]; !ok { if k, ok = typeToInt(l.token); !ok { return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, "" } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, "" } l = <-c } return rr, nil, l.comment } func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(NSEC3) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NSEC3 Hash", l}, "" } else { rr.Hash = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NSEC3 Flags", l}, "" } else { rr.Flags = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NSEC3 Iterations", l}, "" } else { rr.Iterations = uint16(i) } <-c l = <-c if len(l.token) == 0 { return nil, &ParseError{f, "bad NSEC3 Salt", l}, "" } rr.SaltLength = uint8(len(l.token)) / 2 rr.Salt = l.token <-c l = <-c rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) rr.NextDomain = l.token rr.TypeBitMap = make([]uint16, 0) var ( k uint16 ok bool ) l = <-c for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _BLANK: // Ok case _STRING: if k, ok = StringToType[strings.ToUpper(l.token)]; !ok { if k, ok = typeToInt(l.token); !ok { return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, "" } } rr.TypeBitMap = append(rr.TypeBitMap, k) default: return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, "" } l = <-c } return rr, nil, l.comment } func setNSEC3PARAM(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(NSEC3PARAM) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NSEC3PARAM Hash", l} } else { rr.Hash = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NSEC3PARAM Flags", l} } else { rr.Flags = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l} } else { rr.Iterations = uint16(i) } <-c l = <-c rr.SaltLength = uint8(len(l.token)) rr.Salt = l.token return rr, nil } func setEUI48(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(EUI48) rr.Hdr = h l := <-c if len(l.token) != 17 { return nil, &ParseError{f, "bad EUI48 Address", l} } addr := make([]byte, 12) dash := 0 for i := 0; i < 10; i += 2 { addr[i] = l.token[i+dash] addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { return nil, &ParseError{f, "bad EUI48 Address", l} } } addr[10] = l.token[15] addr[11] = l.token[16] if i, e := strconv.ParseUint(string(addr), 16, 48); e != nil { return nil, &ParseError{f, "bad EUI48 Address", l} } else { rr.Address = i } return rr, nil } func setEUI64(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(EUI64) rr.Hdr = h l := <-c if len(l.token) != 23 { return nil, &ParseError{f, "bad EUI64 Address", l} } addr := make([]byte, 16) dash := 0 for i := 0; i < 14; i += 2 { addr[i] = l.token[i+dash] addr[i+1] = l.token[i+1+dash] dash++ if l.token[i+1+dash] != '-' { return nil, &ParseError{f, "bad EUI64 Address", l} } } addr[14] = l.token[21] addr[15] = l.token[22] if i, e := strconv.ParseUint(string(addr), 16, 64); e != nil { return nil, &ParseError{f, "bad EUI68 Address", l} } else { rr.Address = uint64(i) } return rr, nil } func setWKS(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(WKS) rr.Hdr = h l := <-c rr.Address = net.ParseIP(l.token) if rr.Address == nil { return nil, &ParseError{f, "bad WKS Adress", l}, "" } <-c // _BLANK l = <-c proto := "tcp" if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad WKS Protocol", l}, "" } else { rr.Protocol = uint8(i) switch rr.Protocol { case 17: proto = "udp" case 6: proto = "tcp" default: return nil, &ParseError{f, "bad WKS Protocol", l}, "" } } <-c l = <-c rr.BitMap = make([]uint16, 0) var ( k int err error ) for l.value != _NEWLINE && l.value != _EOF { switch l.value { case _BLANK: // Ok case _STRING: if k, err = net.LookupPort(proto, l.token); err != nil { if i, e := strconv.Atoi(l.token); e != nil { // If a number use that rr.BitMap = append(rr.BitMap, uint16(i)) } else { return nil, &ParseError{f, "bad WKS BitMap", l}, "" } } rr.BitMap = append(rr.BitMap, uint16(k)) default: return nil, &ParseError{f, "bad WKS BitMap", l}, "" } l = <-c } return rr, nil, l.comment } func setSSHFP(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(SSHFP) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad SSHFP Algorithm", l} } else { rr.Algorithm = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad SSHFP Type", l} } else { rr.Type = uint8(i) } <-c // _BLANK l = <-c rr.FingerPrint = l.token return rr, nil } func setDNSKEY(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(DNSKEY) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DNSKEY Flags", l}, "" } else { rr.Flags = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DNSKEY Protocol", l}, "" } else { rr.Protocol = uint8(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DNSKEY Algorithm", l}, "" } else { rr.Algorithm = uint8(i) } s, e, c1 := endingToString(c, "bad DNSKEY PublicKey", f) if e != nil { return nil, e, c1 } rr.PublicKey = s return rr, nil, c1 } func setRKEY(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(RKEY) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad RKEY Flags", l}, "" } else { rr.Flags = uint16(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad RKEY Protocol", l}, "" } else { rr.Protocol = uint8(i) } <-c // _BLANK l = <-c // _STRING if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad RKEY Algorithm", l}, "" } else { rr.Algorithm = uint8(i) } s, e, c1 := endingToString(c, "bad RKEY PublicKey", f) if e != nil { return nil, e, c1 } rr.PublicKey = s return rr, nil, c1 } func setDS(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(DS) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DS KeyTag", l}, "" } else { rr.KeyTag = uint16(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { if i, ok := StringToAlgorithm[strings.ToUpper(l.token)]; !ok { return nil, &ParseError{f, "bad DS Algorithm", l}, "" } else { rr.Algorithm = i } } else { rr.Algorithm = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DS DigestType", l}, "" } else { rr.DigestType = uint8(i) } s, e, c1 := endingToString(c, "bad DS Digest", f) if e != nil { return nil, e, c1 } rr.Digest = s return rr, nil, c1 } func setCDS(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(CDS) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad CDS KeyTag", l}, "" } else { rr.KeyTag = uint16(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { if i, ok := StringToAlgorithm[strings.ToUpper(l.token)]; !ok { return nil, &ParseError{f, "bad CDS Algorithm", l}, "" } else { rr.Algorithm = i } } else { rr.Algorithm = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad CDS DigestType", l}, "" } else { rr.DigestType = uint8(i) } s, e, c1 := endingToString(c, "bad CDS Digest", f) if e != nil { return nil, e, c1 } rr.Digest = s return rr, nil, c1 } func setDLV(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(DLV) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DLV KeyTag", l}, "" } else { rr.KeyTag = uint16(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { if i, ok := StringToAlgorithm[strings.ToUpper(l.token)]; !ok { return nil, &ParseError{f, "bad DLV Algorithm", l}, "" } else { rr.Algorithm = i } } else { rr.Algorithm = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad DLV DigestType", l}, "" } else { rr.DigestType = uint8(i) } s, e, c1 := endingToString(c, "bad DLV Digest", f) if e != nil { return nil, e, c1 } rr.Digest = s return rr, nil, c1 } func setTA(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(TA) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad TA KeyTag", l}, "" } else { rr.KeyTag = uint16(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { if i, ok := StringToAlgorithm[strings.ToUpper(l.token)]; !ok { return nil, &ParseError{f, "bad TA Algorithm", l}, "" } else { rr.Algorithm = i } } else { rr.Algorithm = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad TA DigestType", l}, "" } else { rr.DigestType = uint8(i) } s, e, c1 := endingToString(c, "bad TA Digest", f) if e != nil { return nil, e, c1 } rr.Digest = s return rr, nil, c1 } func setTLSA(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(TLSA) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad TLSA Usage", l}, "" } else { rr.Usage = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad TLSA Selector", l}, "" } else { rr.Selector = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad TLSA MatchingType", l}, "" } else { rr.MatchingType = uint8(i) } s, e, c1 := endingToString(c, "bad TLSA Certificate", f) if e != nil { return nil, e, c1 } rr.Certificate = s return rr, nil, c1 } func setRFC3597(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(RFC3597) rr.Hdr = h l := <-c if l.token != "\\#" { return nil, &ParseError{f, "bad RFC3597 Rdata", l}, "" } <-c // _BLANK l = <-c rdlength, e := strconv.Atoi(l.token) if e != nil { return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, "" } s, e1, c1 := endingToString(c, "bad RFC3597 Rdata", f) if e1 != nil { return nil, e1, c1 } if rdlength*2 != len(s) { return nil, &ParseError{f, "bad RFC3597 Rdata", l}, "" } rr.Rdata = s return rr, nil, c1 } func setSPF(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(SPF) rr.Hdr = h s, e, c1 := endingToTxtSlice(c, "bad SPF Txt", f) if e != nil { return nil, e, "" } rr.Txt = s return rr, nil, c1 } func setTXT(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(TXT) rr.Hdr = h // No _BLANK reading here, because this is all rdata is TXT s, e, c1 := endingToTxtSlice(c, "bad TXT Txt", f) if e != nil { return nil, e, "" } rr.Txt = s return rr, nil, c1 } // identical to setTXT func setNINFO(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(NINFO) rr.Hdr = h s, e, c1 := endingToTxtSlice(c, "bad NINFO ZSData", f) if e != nil { return nil, e, "" } rr.ZSData = s return rr, nil, c1 } func setURI(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(URI) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad URI Priority", l}, "" } else { rr.Priority = uint16(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad URI Weight", l}, "" } else { rr.Weight = uint16(i) } <-c // _BLANK s, e, c1 := endingToTxtSlice(c, "bad URI Target", f) if e != nil { return nil, e, "" } rr.Target = s return rr, nil, c1 } func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(IPSECKEY) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, "" } else { rr.Precedence = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" } else { rr.GatewayType = uint8(i) } <-c // _BLANK l = <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, "" } else { rr.Algorithm = uint8(i) } <-c l = <-c rr.Gateway = l.token s, e, c1 := endingToString(c, "bad IPSECKEY PublicKey", f) if e != nil { return nil, e, c1 } rr.PublicKey = s return rr, nil, c1 } func setDHCID(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { // awesome record to parse! rr := new(DHCID) rr.Hdr = h s, e, c1 := endingToString(c, "bad DHCID Digest", f) if e != nil { return nil, e, c1 } rr.Digest = s return rr, nil, c1 } func setNID(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(NID) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad NID Preference", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING u, err := stringToNodeID(l) if err != nil { return nil, err } rr.NodeID = u return rr, nil } func setL32(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(L32) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad L32 Preference", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Locator32 = net.ParseIP(l.token) if rr.Locator32 == nil { return nil, &ParseError{f, "bad L32 Locator", l} } return rr, nil } func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError) { rr := new(LP) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad LP Preference", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING rr.Fqdn = l.token if l.token == "@" { rr.Fqdn = o return rr, nil } _, ok := IsDomainName(l.token) if !ok { return nil, &ParseError{f, "bad LP Fqdn", l} } if rr.Fqdn[l.length-1] != '.' { rr.Fqdn = appendOrigin(rr.Fqdn, o) } return rr, nil } func setL64(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(L64) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad L64 Preference", l} } else { rr.Preference = uint16(i) } <-c // _BLANK l = <-c // _STRING u, err := stringToNodeID(l) if err != nil { return nil, err } rr.Locator64 = u return rr, nil } func setUID(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(UID) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad UID Uid", l} } else { rr.Uid = uint32(i) } return rr, nil } func setGID(h RR_Header, c chan lex, f string) (RR, *ParseError) { rr := new(GID) rr.Hdr = h l := <-c if i, e := strconv.Atoi(l.token); e != nil { return nil, &ParseError{f, "bad GID Gid", l} } else { rr.Gid = uint32(i) } return rr, nil } func setUINFO(h RR_Header, c chan lex, f string) (RR, *ParseError, string) { rr := new(UINFO) rr.Hdr = h s, e, c1 := endingToTxtSlice(c, "bad UINFO Uinfo", f) if e != nil { return nil, e, "" } rr.Uinfo = s[0] // silently discard anything above return rr, nil, c1 }