From d2dd4911ca98eb082114bb26b8ab1bee14f7cdb0 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Wed, 4 May 2016 16:22:06 -0700 Subject: [PATCH 1/6] Godeps changes for kube-dns --- Godeps/Godeps.json | 6 +- vendor/github.com/miekg/dns/.travis.yml | 22 +- vendor/github.com/miekg/dns/README.md | 43 +- vendor/github.com/miekg/dns/client.go | 297 +++-- vendor/github.com/miekg/dns/clientconfig.go | 11 +- vendor/github.com/miekg/dns/defaults.go | 54 +- vendor/github.com/miekg/dns/dns.go | 119 +- vendor/github.com/miekg/dns/dnssec.go | 464 +++---- vendor/github.com/miekg/dns/dnssec_keygen.go | 156 +++ .../miekg/dns/{kscan.go => dnssec_keyscan.go} | 133 +- vendor/github.com/miekg/dns/dnssec_privkey.go | 85 ++ vendor/github.com/miekg/dns/doc.go | 251 ++++ vendor/github.com/miekg/dns/edns.go | 156 +-- vendor/github.com/miekg/dns/format.go | 96 ++ vendor/github.com/miekg/dns/keygen.go | 157 --- vendor/github.com/miekg/dns/labels.go | 12 +- vendor/github.com/miekg/dns/msg.go | 559 ++++---- vendor/github.com/miekg/dns/nsecx.go | 2 + vendor/github.com/miekg/dns/privaterr.go | 23 +- vendor/github.com/miekg/dns/rawmsg.go | 6 +- vendor/github.com/miekg/dns/sanitize.go | 84 ++ vendor/github.com/miekg/dns/server.go | 311 +++-- vendor/github.com/miekg/dns/sig0.go | 74 +- vendor/github.com/miekg/dns/tlsa.go | 8 +- vendor/github.com/miekg/dns/tsig.go | 78 +- vendor/github.com/miekg/dns/types.go | 643 ++------- vendor/github.com/miekg/dns/types_generate.go | 266 ++++ vendor/github.com/miekg/dns/udp.go | 19 +- vendor/github.com/miekg/dns/udp_linux.go | 10 + vendor/github.com/miekg/dns/udp_other.go | 2 +- vendor/github.com/miekg/dns/udp_plan9.go | 34 + vendor/github.com/miekg/dns/udp_windows.go | 14 +- vendor/github.com/miekg/dns/update.go | 130 +- vendor/github.com/miekg/dns/xfr.go | 54 +- vendor/github.com/miekg/dns/zgenerate.go | 33 +- vendor/github.com/miekg/dns/zscan.go | 398 +++--- vendor/github.com/miekg/dns/zscan_rr.go | 1155 +++++++++-------- vendor/github.com/miekg/dns/ztypes.go | 842 ++++++++++++ .../skynetservices/skydns/msg/service.go | 58 +- 39 files changed, 4162 insertions(+), 2703 deletions(-) create mode 100644 vendor/github.com/miekg/dns/dnssec_keygen.go rename vendor/github.com/miekg/dns/{kscan.go => dnssec_keyscan.go} (73%) create mode 100644 vendor/github.com/miekg/dns/dnssec_privkey.go create mode 100644 vendor/github.com/miekg/dns/doc.go create mode 100644 vendor/github.com/miekg/dns/format.go delete mode 100644 vendor/github.com/miekg/dns/keygen.go create mode 100644 vendor/github.com/miekg/dns/sanitize.go create mode 100644 vendor/github.com/miekg/dns/types_generate.go create mode 100644 vendor/github.com/miekg/dns/udp_plan9.go create mode 100644 vendor/github.com/miekg/dns/ztypes.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0744a05c12e..92511653a1d 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1335,7 +1335,7 @@ }, { "ImportPath": "github.com/miekg/dns", - "Rev": "3f504e8dabd5d562e997d19ce0200aa41973e1b2" + "Rev": "c2b278e70f35902fd68b54b69238cd10bb1b7451" }, { "ImportPath": "github.com/mistifyio/go-zfs", @@ -1779,8 +1779,8 @@ }, { "ImportPath": "github.com/skynetservices/skydns/msg", - "Comment": "2.5.1a", - "Rev": "1be70b5b8aa07acccd972146d84011b670af88b4" + "Comment": "2.5.3a-32-gf7b6fb7", + "Rev": "f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78" }, { "ImportPath": "github.com/spf13/cobra", diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml index 4485679769d..1f056ab7ccc 100644 --- a/vendor/github.com/miekg/dns/.travis.yml +++ b/vendor/github.com/miekg/dns/.travis.yml @@ -1,21 +1,7 @@ language: go +sudo: false go: - - 1.2 - - 1.3 -env: - # "gvm update" resets GOOS and GOARCH environment variable, workaround it by setting - # BUILD_GOOS and BUILD_GOARCH and overriding GOARCH and GOOS in the build script - global: - - BUILD_GOARCH=amd64 - matrix: - - BUILD_GOOS=linux - - BUILD_GOOS=darwin - - BUILD_GOOS=windows + - 1.5 + - 1.6 script: - - gvm cross $BUILD_GOOS $BUILD_GOARCH - - GOARCH=$BUILD_GOARCH GOOS=$BUILD_GOOS go build - - # only test on linux - # also specify -short; the crypto tests fail in weird ways *sometimes* - # See issue #151 - - if [ $BUILD_GOOS == "linux" ]; then GOARCH=$BUILD_GOARCH GOOS=$BUILD_GOOS go test -short -bench=.; fi + - go test -race -v -bench=. diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 3cb850a0b07..011d085f18d 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -10,9 +10,9 @@ If there is stuff you should know as a DNS programmer there isn't a convenience function for it. Server side and client side programming is supported, i.e. you can build servers and resolvers with it. -If you like this, you may also be interested in: - -* https://github.com/miekg/unbound -- Go wrapper for the Unbound resolver. +We try to keep the "master" branch as sane as possible and at the bleeding edge +of standards, avoiding breaking changes wherever reasonable. We support the last +two versions of Go, currently: 1.4 and 1.5. # Goals @@ -24,6 +24,7 @@ If you like this, you may also be interested in: A not-so-up-to-date-list-that-may-be-actually-current: +* https://cloudflare.com * https://github.com/abh/geodns * http://www.statdns.com/ * http://www.dnsinspect.com/ @@ -32,8 +33,21 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/fcambus/rrda * https://github.com/kenshinx/godns * https://github.com/skynetservices/skydns +* https://github.com/hashicorp/consul * https://github.com/DevelopersPL/godnsagent * https://github.com/duedil-ltd/discodns +* https://github.com/StalkR/dns-reverse-proxy +* https://github.com/tianon/rawdns +* https://mesosphere.github.io/mesos-dns/ +* https://pulse.turbobytes.com/ +* https://play.google.com/store/apps/details?id=com.turbobytes.dig +* https://github.com/fcambus/statzone +* https://github.com/benschw/dns-clb-go +* https://github.com/corny/dnscheck for http://public-dns.info/ +* https://namesmith.io +* https://github.com/miekg/unbound +* https://github.com/miekg/exdns +* https://dnslookup.org Send pull request if you want to be listed here. @@ -50,6 +64,7 @@ Send pull request if you want to be listed here. * EDNS0, NSID; * AXFR/IXFR; * TSIG, SIG(0); +* DNS over TLS: optional encrypted connection between client and server; * DNS name compression; * Depends only on the standard library. @@ -67,7 +82,7 @@ correctly, the following should work: ## Examples -A short "how to use the API" is at the beginning of dns.go (this also will show +A short "how to use the API" is at the beginning of doc.go (this also will show when you call `godoc github.com/miekg/dns`). Example programs can be found in the `github.com/miekg/exdns` repository. @@ -77,7 +92,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. *all of them* * 103{4,5} - DNS standard -* 1348 - NSAP record +* 1348 - NSAP record (removed the record) * 1982 - Serial Arithmetic * 1876 - LOC record * 1995 - IXFR @@ -95,7 +110,8 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 3225 - DO bit (DNSSEC OK) * 340{1,2,3} - NAPTR record * 3445 - Limiting the scope of (DNS)KEY -* 3597 - Unkown RRs +* 3597 - Unknown RRs +* 4025 - IPSECKEY * 403{3,4,5} - DNSSEC + validation functions * 4255 - SSHFP record * 4343 - Case insensitivity @@ -114,13 +130,16 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 6605 - ECDSA * 6725 - IANA Registry Update * 6742 - ILNP DNS +* 6840 - Clarifications and Implementation Notes for DNS Security +* 6844 - CAA record * 6891 - EDNS0 update * 6895 - DNS IANA considerations * 6975 - Algorithm Understanding in DNSSEC * 7043 - EUI48/EUI64 records * 7314 - DNS (EDNS) EXPIRE Option -* xxxx - URI record (draft) +* 7553 - URI record * xxxx - EDNS0 DNS Update Lease (draft) +* yyyy - DNS over TLS: Initiation and Performance Considerations (draft) ## Loosely based upon @@ -132,9 +151,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. ## TODO * privatekey.Precompute() when signing? -* Last remaining RRs: APL, ATMA, A6 and NXT; -* Missing in parsing: ISDN, UNSPEC, ATMA; -* CAA parsing is broken; -* NSEC(3) cover/match/closest enclose; -* Replies with TC bit are not parsed to the end; -* Create IsMsg to validate a message before fully parsing it. +* Last remaining RRs: APL, ATMA, A6, NSAP and NXT. +* Missing in parsing: ISDN, UNSPEC, NSAP and ATMA. +* NSEC(3) cover/match/closest enclose. +* Replies with TC bit are not parsed to the end. diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index ee8e22333b1..7a05eb99cc7 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -4,12 +4,13 @@ package dns import ( "bytes" + "crypto/tls" "io" "net" "time" ) -const dnsTimeout time.Duration = 2 * 1e9 +const dnsTimeout time.Duration = 2 * time.Second const tcpIdleTimeout time.Duration = 8 * time.Second // A Conn represents a connection to a DNS server. @@ -24,11 +25,12 @@ type Conn struct { // A Client defines parameters for a DNS client. type Client struct { - Net string // if "tcp" a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) + Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) UDPSize uint16 // minimum receive buffer for UDP messages - DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 + TLSConfig *tls.Config // TLS connection configuration + DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass group singleflight @@ -37,14 +39,7 @@ type Client struct { // Exchange performs a synchronous UDP query. It sends the message m to the address // contained in a and waits for an reply. Exchange does not retry a failed query, nor // will it fall back to TCP in case of truncation. -// If you need to send a DNS message on an already existing connection, you can use the -// following: -// -// co := &dns.Conn{Conn: c} // c is your net.Conn -// co.WriteMsg(m) -// in, err := co.ReadMsg() -// co.Close() -// +// See client.Exchange for more information on setting larger buffer sizes. func Exchange(m *Msg, a string) (r *Msg, err error) { var co *Conn co, err = DialTimeout("udp", a, dnsTimeout) @@ -53,12 +48,23 @@ func Exchange(m *Msg, a string) (r *Msg, err error) { } defer co.Close() - co.SetReadDeadline(time.Now().Add(dnsTimeout)) + + opt := m.IsEdns0() + // If EDNS0 is used use that for size. + if opt != nil && opt.UDPSize() >= MinMsgSize { + co.UDPSize = opt.UDPSize() + } + co.SetWriteDeadline(time.Now().Add(dnsTimeout)) if err = co.WriteMsg(m); err != nil { return nil, err } + + co.SetReadDeadline(time.Now().Add(dnsTimeout)) r, err = co.ReadMsg() + if err == nil && r.Id != m.Id { + err = ErrId + } return r, err } @@ -79,6 +85,9 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { return nil, err } r, err = co.ReadMsg() + if err == nil && r.Id != m.Id { + err = ErrId + } return r, err } @@ -90,6 +99,10 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { // // Exchange does not retry a failed query, nor will it fall back to TCP in // case of truncation. +// It is up to the caller to create a message that allows for larger responses to be +// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger +// buffer, see SetEdns0. Messsages without an OPT RR will fallback to the historic limit +// of 512 bytes. func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { return c.exchange(m, a) @@ -115,31 +128,59 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro return r, rtt, nil } -func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - timeout := dnsTimeout - var co *Conn +func (c *Client) dialTimeout() time.Duration { if c.DialTimeout != 0 { - timeout = c.DialTimeout + return c.DialTimeout } - if c.Net == "" { - co, err = DialTimeout("udp", a, timeout) + return dnsTimeout +} + +func (c *Client) readTimeout() time.Duration { + if c.ReadTimeout != 0 { + return c.ReadTimeout + } + return dnsTimeout +} + +func (c *Client) writeTimeout() time.Duration { + if c.WriteTimeout != 0 { + return c.WriteTimeout + } + return dnsTimeout +} + +func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { + var co *Conn + network := "udp" + tls := false + + switch c.Net { + case "tcp-tls": + network = "tcp" + tls = true + case "tcp4-tls": + network = "tcp4" + tls = true + case "tcp6-tls": + network = "tcp6" + tls = true + default: + if c.Net != "" { + network = c.Net + } + } + + if tls { + co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout()) } else { - co, err = DialTimeout(c.Net, a, timeout) + co, err = DialTimeout(network, a, c.dialTimeout()) } + if err != nil { return nil, 0, err } - timeout = dnsTimeout - if c.ReadTimeout != 0 { - timeout = c.ReadTimeout - } - co.SetReadDeadline(time.Now().Add(timeout)) - timeout = dnsTimeout - if c.WriteTimeout != 0 { - timeout = c.WriteTimeout - } - co.SetWriteDeadline(time.Now().Add(timeout)) defer co.Close() + opt := m.IsEdns0() // If EDNS0 is used use that for size. if opt != nil && opt.UDPSize() >= MinMsgSize { @@ -149,11 +190,18 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro if opt == nil && c.UDPSize >= MinMsgSize { co.UDPSize = c.UDPSize } + co.TsigSecret = c.TsigSecret + co.SetWriteDeadline(time.Now().Add(c.writeTimeout())) if err = co.WriteMsg(m); err != nil { return nil, 0, err } + + co.SetReadDeadline(time.Now().Add(c.readTimeout())) r, err = co.ReadMsg() + if err == nil && r.Id != m.Id { + err = ErrId + } return r, co.rtt, err } @@ -161,26 +209,21 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro // If the received message contains a TSIG record the transaction // signature is verified. func (co *Conn) ReadMsg() (*Msg, error) { - var p []byte + p, err := co.ReadMsgHeader(nil) + if err != nil { + return nil, err + } + m := new(Msg) - if _, ok := co.Conn.(*net.TCPConn); ok { - p = make([]byte, MaxMsgSize) - } else { - if co.UDPSize >= 512 { - p = make([]byte, co.UDPSize) - } else { - p = make([]byte, MinMsgSize) - } - } - n, err := co.Read(p) - if err != nil && n == 0 { - return nil, err - } - p = p[:n] if err := m.Unpack(p); err != nil { + // If ErrTruncated was returned, we still want to allow the user to use + // the message, but naively they can just check err if they don't want + // to use a truncated message + if err == ErrTruncated { + return m, err + } return nil, err } - co.rtt = time.Since(co.t) if t := m.IsTsig(); t != nil { if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { return m, ErrSecret @@ -191,6 +234,86 @@ func (co *Conn) ReadMsg() (*Msg, error) { return m, err } +// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil). +// Returns message as a byte slice to be parsed with Msg.Unpack later on. +// Note that error handling on the message body is not possible as only the header is parsed. +func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { + var ( + p []byte + n int + err error + ) + + switch t := co.Conn.(type) { + case *net.TCPConn, *tls.Conn: + r := t.(io.Reader) + + // First two bytes specify the length of the entire message. + l, err := tcpMsgLen(r) + if err != nil { + return nil, err + } + p = make([]byte, l) + n, err = tcpRead(r, p) + co.rtt = time.Since(co.t) + default: + if co.UDPSize > MinMsgSize { + p = make([]byte, co.UDPSize) + } else { + p = make([]byte, MinMsgSize) + } + n, err = co.Read(p) + co.rtt = time.Since(co.t) + } + + if err != nil { + return nil, err + } else if n < headerSize { + return nil, ErrShortRead + } + + p = p[:n] + if hdr != nil { + if _, err = UnpackStruct(hdr, p, 0); err != nil { + return nil, err + } + } + return p, err +} + +// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length. +func tcpMsgLen(t io.Reader) (int, error) { + p := []byte{0, 0} + n, err := t.Read(p) + if err != nil { + return 0, err + } + if n != 2 { + return 0, ErrShortRead + } + l, _ := unpackUint16(p, 0) + if l == 0 { + return 0, ErrShortRead + } + return int(l), nil +} + +// tcpRead calls TCPConn.Read enough times to fill allocated buffer. +func tcpRead(t io.Reader, p []byte) (int, error) { + n, err := t.Read(p) + if err != nil { + return n, err + } + for n < len(p) { + j, err := t.Read(p[n:]) + if err != nil { + return n, err + } + n += j + } + return n, err +} + // Read implements the net.Conn read method. func (co *Conn) Read(p []byte) (n int, err error) { if co.Conn == nil { @@ -199,32 +322,18 @@ func (co *Conn) Read(p []byte) (n int, err error) { if len(p) < 2 { return 0, io.ErrShortBuffer } - if t, ok := co.Conn.(*net.TCPConn); ok { - n, err = t.Read(p[0:2]) - if err != nil || n != 2 { - return n, err + switch t := co.Conn.(type) { + case *net.TCPConn, *tls.Conn: + r := t.(io.Reader) + + l, err := tcpMsgLen(r) + if err != nil { + return 0, err } - l, _ := unpackUint16(p[0:2], 0) - if l == 0 { - return 0, ErrShortRead - } - if int(l) > len(p) { + if l > len(p) { return int(l), io.ErrShortBuffer } - n, err = t.Read(p[:l]) - if err != nil { - return n, err - } - i := n - for i < int(l) { - j, err := t.Read(p[i:int(l)]) - if err != nil { - return i, err - } - i += j - } - n = i - return n, err + return tcpRead(r, p[:l]) } // UDP connection n, err = co.Conn.Read(p) @@ -234,7 +343,7 @@ func (co *Conn) Read(p []byte) (n int, err error) { return n, err } -// WriteMsg sends a message throught the connection co. +// WriteMsg sends a message through the connection co. // If the message m contains a TSIG record the transaction // signature is calculated. func (co *Conn) WriteMsg(m *Msg) (err error) { @@ -245,7 +354,7 @@ func (co *Conn) WriteMsg(m *Msg) (err error) { return ErrSecret } out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) - // Set for the next read, allthough only used in zone transfers + // Set for the next read, although only used in zone transfers co.tsigRequestMAC = mac } else { out, err = m.Pack() @@ -262,7 +371,10 @@ func (co *Conn) WriteMsg(m *Msg) (err error) { // Write implements the net.Conn Write method. func (co *Conn) Write(p []byte) (n int, err error) { - if t, ok := co.Conn.(*net.TCPConn); ok { + switch t := co.Conn.(type) { + case *net.TCPConn, *tls.Conn: + w := t.(io.Writer) + lp := len(p) if lp < 2 { return 0, io.ErrShortBuffer @@ -273,7 +385,7 @@ func (co *Conn) Write(p []byte) (n int, err error) { l := make([]byte, 2, lp+2) l[0], l[1] = packUint16(uint16(lp)) p = append(l, p...) - n, err := io.Copy(t, bytes.NewReader(p)) + n, err := io.Copy(w, bytes.NewReader(p)) return int(n), err } n, err = co.Conn.(*net.UDPConn).Write(p) @@ -290,7 +402,7 @@ func Dial(network, address string) (conn *Conn, err error) { return conn, nil } -// Dialtimeout acts like Dial but takes a timeout. +// DialTimeout acts like Dial but takes a timeout. func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) { conn = new(Conn) conn.Conn, err = net.DialTimeout(network, address, timeout) @@ -300,20 +412,25 @@ func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, er return conn, nil } -// Close implements the net.Conn Close method. -func (co *Conn) Close() error { return co.Conn.Close() } +// DialWithTLS connects to the address on the named network with TLS. +func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) { + conn = new(Conn) + conn.Conn, err = tls.Dial(network, address, tlsConfig) + if err != nil { + return nil, err + } + return conn, nil +} -// LocalAddr implements the net.Conn LocalAddr method. -func (co *Conn) LocalAddr() net.Addr { return co.Conn.LocalAddr() } +// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout. +func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) { + var dialer net.Dialer + dialer.Timeout = timeout -// RemoteAddr implements the net.Conn RemoteAddr method. -func (co *Conn) RemoteAddr() net.Addr { return co.Conn.RemoteAddr() } - -// SetDeadline implements the net.Conn SetDeadline method. -func (co *Conn) SetDeadline(t time.Time) error { return co.Conn.SetDeadline(t) } - -// SetReadDeadline implements the net.Conn SetReadDeadline method. -func (co *Conn) SetReadDeadline(t time.Time) error { return co.Conn.SetReadDeadline(t) } - -// SetWriteDeadline implements the net.Conn SetWriteDeadline method. -func (co *Conn) SetWriteDeadline(t time.Time) error { return co.Conn.SetWriteDeadline(t) } + conn = new(Conn) + conn.Conn, err = tls.DialWithDialer(&dialer, network, address, tlsConfig) + if err != nil { + return nil, err + } + return conn, nil +} diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go index 87cf8961862..cfa9ad0b228 100644 --- a/vendor/github.com/miekg/dns/clientconfig.go +++ b/vendor/github.com/miekg/dns/clientconfig.go @@ -7,7 +7,7 @@ import ( "strings" ) -// Wraps the contents of the /etc/resolv.conf. +// ClientConfig wraps the contents of the /etc/resolv.conf file. type ClientConfig struct { Servers []string // servers to use Search []string // suffixes to append to local name @@ -26,14 +26,19 @@ func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) { } defer file.Close() c := new(ClientConfig) - b := bufio.NewReader(file) + scanner := bufio.NewScanner(file) c.Servers = make([]string, 0) c.Search = make([]string, 0) c.Port = "53" c.Ndots = 1 c.Timeout = 5 c.Attempts = 2 - for line, ok := b.ReadString('\n'); ok == nil; line, ok = b.ReadString('\n') { + + for scanner.Scan() { + if err := scanner.Err(); err != nil { + return nil, err + } + line := scanner.Text() f := strings.Fields(line) if len(f) < 1 { continue diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index 0c8fa9c84b6..63165b4fa9c 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -24,7 +24,9 @@ func (dns *Msg) SetReply(request *Msg) *Msg { return dns } -// SetQuestion creates a question message. +// SetQuestion creates a question message, it sets the Question +// section, generates an Id and sets the RecursionDesired (RD) +// bit to true. func (dns *Msg) SetQuestion(z string, t uint16) *Msg { dns.Id = Id() dns.RecursionDesired = true @@ -33,7 +35,9 @@ func (dns *Msg) SetQuestion(z string, t uint16) *Msg { return dns } -// SetNotify creates a notify message. +// SetNotify creates a notify message, it sets the Question +// section, generates an Id and sets the Authoritative (AA) +// bit to true. func (dns *Msg) SetNotify(z string) *Msg { dns.Opcode = OpcodeNotify dns.Authoritative = true @@ -73,13 +77,15 @@ func (dns *Msg) SetUpdate(z string) *Msg { } // SetIxfr creates message for requesting an IXFR. -func (dns *Msg) SetIxfr(z string, serial uint32) *Msg { +func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg { dns.Id = Id() dns.Question = make([]Question, 1) dns.Ns = make([]RR, 1) s := new(SOA) s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0} s.Serial = serial + s.Ns = ns + s.Mbox = mbox dns.Question[0] = Question{z, TypeIXFR, ClassINET} dns.Ns[0] = s return dns @@ -144,11 +150,14 @@ func (dns *Msg) IsEdns0() *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. +// IsDomainName checks if s is a valid domain name, it returns the number of +// labels and true, when a domain name is valid. Note that non fully qualified +// domain name is considered valid, in this case the last label is counted in +// the number of labels. When false is returned the number of labels is not +// defined. Also note that this function is extremely liberal; almost any +// string is a valid domain name as the DNS is 8 bit protocol. It checks if each +// label fits in 63 characters, but there is no length check for the entire +// string s. I.e. a domain name longer than 255 characters is considered valid. func IsDomainName(s string) (labels int, ok bool) { _, labels, err := packDomainName(s, nil, 0, nil, false) return labels, err == nil @@ -182,7 +191,34 @@ func IsFqdn(s string) bool { return s[l-1] == '.' } -// Fqdns return the fully qualified domain name from s. +// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181. +// This means the RRs need to have the same type, name, and class. Returns true +// if the RR set is valid, otherwise false. +func IsRRset(rrset []RR) bool { + if len(rrset) == 0 { + return false + } + if len(rrset) == 1 { + return true + } + rrHeader := rrset[0].Header() + rrType := rrHeader.Rrtype + rrClass := rrHeader.Class + rrName := rrHeader.Name + + for _, rr := range rrset[1:] { + curRRHeader := rr.Header() + if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName { + // Mismatch between the records, so this is not a valid rrset for + //signing/verifying + return false + } + } + + return true +} + +// Fqdn return the fully qualified domain name from s. // If s is already fully qualified, it behaves as the identity function. func Fqdn(s string) string { if IsFqdn(s) { diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go index 7540c0d5bca..a3e4a0efae5 100644 --- a/vendor/github.com/miekg/dns/dns.go +++ b/vendor/github.com/miekg/dns/dns.go @@ -1,108 +1,16 @@ -// Package dns implements a full featured interface to the Domain Name System. -// Server- and client-side programming is supported. -// The package allows complete control over what is send out to the DNS. The package -// API follows the less-is-more principle, by presenting a small, clean interface. -// -// The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers, -// TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing. -// Note that domain names MUST be fully qualified, before sending them, unqualified -// names in a message will result in a packing failure. -// -// Resource records are native types. They are not stored in wire format. -// Basic usage pattern for creating a new resource record: -// -// r := new(dns.MX) -// r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} -// r.Preference = 10 -// r.Mx = "mx.miek.nl." -// -// Or directly from a string: -// -// mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") -// -// Or when the default TTL (3600) and class (IN) suit you: -// -// mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.") -// -// Or even: -// -// mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") -// -// In the DNS messages are exchanged, these messages contain resource -// records (sets). Use pattern for creating a message: -// -// m := new(dns.Msg) -// m.SetQuestion("miek.nl.", dns.TypeMX) -// -// Or when not certain if the domain name is fully qualified: -// -// m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX) -// -// The message m is now a message with the question section set to ask -// the MX records for the miek.nl. zone. -// -// The following is slightly more verbose, but more flexible: -// -// m1 := new(dns.Msg) -// m1.Id = dns.Id() -// m1.RecursionDesired = true -// m1.Question = make([]dns.Question, 1) -// m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} -// -// After creating a message it can be send. -// Basic use pattern for synchronous querying the DNS at a -// server configured on 127.0.0.1 and port 53: -// -// c := new(dns.Client) -// in, rtt, err := c.Exchange(m1, "127.0.0.1:53") -// -// Suppressing -// multiple outstanding queries (with the same question, type and class) is as easy as setting: -// -// c.SingleInflight = true -// -// If these "advanced" features are not needed, a simple UDP query can be send, -// with: -// -// in, err := dns.Exchange(m1, "127.0.0.1:53") -// -// When this functions returns you will get dns message. A dns message consists -// out of four sections. -// The question section: in.Question, the answer section: in.Answer, -// the authority section: in.Ns and the additional section: in.Extra. -// -// Each of these sections (except the Question section) contain a []RR. Basic -// use pattern for accessing the rdata of a TXT RR as the first RR in -// the Answer section: -// -// if t, ok := in.Answer[0].(*dns.TXT); ok { -// // do something with t.Txt -// } -// -// Domain Name and TXT Character String Representations -// -// Both domain names and TXT character strings are converted to presentation -// form both when unpacked and when converted to strings. -// -// For TXT character strings, tabs, carriage returns and line feeds will be -// converted to \t, \r and \n respectively. Back slashes and quotations marks -// will be escaped. Bytes below 32 and above 127 will be converted to \DDD -// form. -// -// For domain names, in addition to the above rules brackets, periods, -// spaces, semicolons and the at symbol are escaped. package dns -import ( - "strconv" -) +import "strconv" const ( - year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. - DefaultMsgSize = 4096 // Standard default for larger than 512 bytes. - MinMsgSize = 512 // Minimal size of a DNS packet. - MaxMsgSize = 65536 // Largest possible DNS packet. - defaultTtl = 3600 // Default TTL. + year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. + // DefaultMsgSize is the standard default for messages larger than 512 bytes. + DefaultMsgSize = 4096 + // MinMsgSize is the minimal size of a DNS packet. + MinMsgSize = 512 + // MaxMsgSize is the largest possible DNS packet. + MaxMsgSize = 65535 + defaultTtl = 3600 // Default internal TTL. ) // Error represents a DNS error @@ -124,13 +32,11 @@ type RR interface { 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 returns the length (in octets) 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. +// RR_Header is the header all DNS resource records share. type RR_Header struct { Name string `dns:"cdomain-name"` Rrtype uint16 @@ -139,9 +45,10 @@ type RR_Header struct { Rdlength uint16 // length of data after header } +// Header returns itself. This is here to make RR_Header implement the RR interface. func (h *RR_Header) Header() *RR_Header { return h } -// Just to imlement the RR interface +// Just to imlement the RR interface. func (h *RR_Header) copy() RR { return nil } func (h *RR_Header) copyHeader() *RR_Header { diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index d1c2ae62582..84cb21421b9 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -1,16 +1,3 @@ -// 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 ( @@ -19,15 +6,14 @@ import ( "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" - "crypto/md5" + _ "crypto/md5" "crypto/rand" "crypto/rsa" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/asn1" "encoding/hex" - "hash" - "io" "math/big" "sort" "strings" @@ -56,6 +42,38 @@ const ( PRIVATEOID uint8 = 254 ) +// Map for algorithm names. +var AlgorithmToString = map[uint8]string{ + RSAMD5: "RSAMD5", + DH: "DH", + DSA: "DSA", + RSASHA1: "RSASHA1", + DSANSEC3SHA1: "DSA-NSEC3-SHA1", + RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1", + RSASHA256: "RSASHA256", + RSASHA512: "RSASHA512", + ECCGOST: "ECC-GOST", + ECDSAP256SHA256: "ECDSAP256SHA256", + ECDSAP384SHA384: "ECDSAP384SHA384", + INDIRECT: "INDIRECT", + PRIVATEDNS: "PRIVATEDNS", + PRIVATEOID: "PRIVATEOID", +} + +// Map of algorithm strings. +var StringToAlgorithm = reverseInt8(AlgorithmToString) + +// Map of algorithm crypto hashes. +var AlgorithmToHash = map[uint8]crypto.Hash{ + RSAMD5: crypto.MD5, // Deprecated in RFC 6725 + RSASHA1: crypto.SHA1, + RSASHA1NSEC3SHA1: crypto.SHA1, + RSASHA256: crypto.SHA256, + ECDSAP256SHA256: crypto.SHA256, + ECDSAP384SHA384: crypto.SHA384, + RSASHA512: crypto.SHA512, +} + // DNSSEC hashing algorithm codes. const ( _ uint8 = iota @@ -66,6 +84,18 @@ const ( SHA512 // Experimental ) +// Map for hash names. +var HashToString = map[uint8]string{ + SHA1: "SHA1", + SHA256: "SHA256", + GOST94: "GOST94", + SHA384: "SHA384", + SHA512: "SHA512", +} + +// Map of hash strings. +var StringToHash = reverseInt8(HashToString) + // DNSKEY flag values. const ( SEP = 1 @@ -74,7 +104,7 @@ const ( ) // The RRSIG needs to be converted to wireformat with some of -// the rdata (the signature) missing. Use this struct to easy +// the rdata (the signature) missing. Use this struct to ease // the conversion (and re-use the pack/unpack functions). type rrsigWireFmt struct { TypeCovered uint16 @@ -182,35 +212,49 @@ func (k *DNSKEY) ToDS(h uint8) *DS { // digest buffer digest := append(owner, wire...) // another copy + var hash crypto.Hash switch h { case SHA1: - s := sha1.New() - io.WriteString(s, string(digest)) - ds.Digest = hex.EncodeToString(s.Sum(nil)) + hash = crypto.SHA1 case SHA256: - s := sha256.New() - io.WriteString(s, string(digest)) - ds.Digest = hex.EncodeToString(s.Sum(nil)) + hash = crypto.SHA256 case SHA384: - s := sha512.New384() - io.WriteString(s, string(digest)) - ds.Digest = hex.EncodeToString(s.Sum(nil)) - case GOST94: - /* I have no clue */ + hash = crypto.SHA384 + case SHA512: + hash = crypto.SHA512 default: return nil } + + s := hash.New() + s.Write(digest) + ds.Digest = hex.EncodeToString(s.Sum(nil)) return ds } -// Sign signs an RRSet. The signature needs to be filled in with -// the values: Inception, Expiration, KeyTag, SignerName and Algorithm. -// The rest is copied from the RRset. Sign returns true when the signing went OK, -// otherwise false. -// There is no check if RRSet is a proper (RFC 2181) RRSet. -// If OrigTTL is non zero, it is used as-is, otherwise the TTL of the RRset -// is used as the OrigTTL. -func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error { +// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record. +func (k *DNSKEY) ToCDNSKEY() *CDNSKEY { + c := &CDNSKEY{DNSKEY: *k} + c.Hdr = *k.Hdr.copyHeader() + c.Hdr.Rrtype = TypeCDNSKEY + return c +} + +// ToCDS converts a DS record to a CDS record. +func (d *DS) ToCDS() *CDS { + c := &CDS{DS: *d} + c.Hdr = *d.Hdr.copyHeader() + c.Hdr.Rrtype = TypeCDS + return c +} + +// Sign signs an RRSet. The signature needs to be filled in with the values: +// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied +// from the RRset. Sign returns a non-nill error when the signing went OK. +// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non +// zero, it is used as-is, otherwise the TTL of the RRset is used as the +// OrigTTL. +func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { if k == nil { return ErrPrivKey } @@ -256,72 +300,72 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error { } signdata = append(signdata, wire...) - var sighash []byte - var h hash.Hash - var ch crypto.Hash // Only need for RSA - var intlen int - 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 - intlen = 32 - case ECDSAP384SHA384: - h = sha512.New384() - intlen = 48 - case RSASHA512: - h = sha512.New() - ch = crypto.SHA512 - case RSAMD5: - fallthrough // Deprecated in RFC 6725 - default: + hash, ok := AlgorithmToHash[rr.Algorithm] + if !ok { 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, intToBytes(r1, 20)...) - signature = append(signature, intToBytes(s1, 20)...) - rr.Signature = toBase64(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 = toBase64(signature) - case *ecdsa.PrivateKey: - r1, s1, err := ecdsa.Sign(rand.Reader, p, sighash) - if err != nil { - return err - } - signature := intToBytes(r1, intlen) - signature = append(signature, intToBytes(s1, intlen)...) - rr.Signature = toBase64(signature) - default: - // Not given the correct key - return ErrKeyAlg + h := hash.New() + h.Write(signdata) + + signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm) + if err != nil { + return err } + + rr.Signature = toBase64(signature) + return nil } +func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) { + signature, err := k.Sign(rand.Reader, hashed, hash) + if err != nil { + return nil, err + } + + switch alg { + case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512: + return signature, nil + + case ECDSAP256SHA256, ECDSAP384SHA384: + ecdsaSignature := &struct { + R, S *big.Int + }{} + if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil { + return nil, err + } + + var intlen int + switch alg { + case ECDSAP256SHA256: + intlen = 32 + case ECDSAP384SHA384: + intlen = 48 + } + + signature := intToBytes(ecdsaSignature.R, intlen) + signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...) + return signature, nil + + // There is no defined interface for what a DSA backed crypto.Signer returns + case DSA, DSANSEC3SHA1: + // t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8) + // signature := []byte{byte(t)} + // signature = append(signature, intToBytes(r1, 20)...) + // signature = append(signature, intToBytes(s1, 20)...) + // rr.Signature = signature + } + + return nil, ErrAlg +} + // Verify validates an RRSet with the signature and key. This is only the // cryptographic test, the signature validity period must be checked separately. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { // First the easy checks - if len(rrset) == 0 { + if !IsRRset(rrset) { return ErrRRset } if rr.KeyTag != k.KeyTag() { @@ -339,14 +383,17 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { 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 - } + + // IsRRset checked that we have at least one RR and that the RRs in + // the set have consistent type, class, and name. Also check that type and + // class matches the RRSIG record. + if rrset[0].Header().Class != rr.Hdr.Class { + return ErrRRset } + if rrset[0].Header().Rrtype != rr.TypeCovered { + return ErrRRset + } + // RFC 4035 5.3.2. Reconstructing the Signed Data // Copy the sig, except the rrsig data sigwire := new(rrsigWireFmt) @@ -373,8 +420,13 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { sigbuf := rr.sigBuf() // Get the binary signature data if rr.Algorithm == PRIVATEDNS { // PRIVATEOID - // TODO(mg) - // remove the domain name and assume its our + // TODO(miek) + // remove the domain name and assume its ours? + } + + hash, ok := AlgorithmToHash[rr.Algorithm] + if !ok { + return ErrAlg } switch rr.Algorithm { @@ -384,57 +436,37 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { 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) + + h := hash.New() + h.Write(signeddata) + return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf) + case ECDSAP256SHA256, ECDSAP384SHA384: - pubkey := k.publicKeyCurve() + pubkey := k.publicKeyECDSA() if pubkey == nil { return ErrKey } - var h hash.Hash - switch rr.Algorithm { - case ECDSAP256SHA256: - h = sha256.New() - case ECDSAP384SHA384: - h = sha512.New384() - } - 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) { + r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2]) + s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:]) + + h := hash.New() + h.Write(signeddata) + if ecdsa.Verify(pubkey, h.Sum(nil), r, s) { return nil } return ErrSig + + default: + return ErrAlg } - // Unknown alg - return ErrAlg } // ValidityPeriod uses RFC1982 serial arithmetic to calculate // if a signature period is valid. If t is the zero time, the -// current time is taken other t is. +// current time is taken other t is. Returns true if the signature +// is valid at the given time, otherwise returns false. func (rr *RRSIG) ValidityPeriod(t time.Time) bool { var utc int64 if t.IsZero() { @@ -450,39 +482,14 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool { } // Return the signatures base64 encodedig sigdata as a byte slice. -func (s *RRSIG) sigBuf() []byte { - sigbuf, err := fromBase64([]byte(s.Signature)) +func (rr *RRSIG) sigBuf() []byte { + sigbuf, err := fromBase64([]byte(rr.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 := fromBase64([]byte(k.PublicKey)) @@ -521,8 +528,8 @@ func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey { return pubkey } -// publicKeyCurve returns the Curve public key from the DNSKEY record. -func (k *DNSKEY) publicKeyCurve() *ecdsa.PublicKey { +// publicKeyECDSA returns the Curve public key from the DNSKEY record. +func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey { keybuf, err := fromBase64([]byte(k.PublicKey)) if err != nil { return nil @@ -573,81 +580,6 @@ func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey { 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 = toBase64(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 - } - var intlen int - switch k.Algorithm { - case ECDSAP256SHA256: - intlen = 32 - case ECDSAP384SHA384: - intlen = 48 - } - k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen)) - return true -} - -// Set the public key for DSA -func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool { - if _Q == nil || _P == nil || _G == nil || _Y == nil { - return false - } - buf := dsaToBuf(_Q, _P, _G, _Y) - k.PublicKey = toBase64(buf) - return true -} - -// Set the public key (the values E and N) for RSA -// RFC 3110: Section 2. RSA Public KEY Resource Records -func exponentToBuf(_E int) []byte { - var buf []byte - i := big.NewInt(int64(_E)) - if len(i.Bytes()) < 256 { - buf = make([]byte, 1) - buf[0] = uint8(len(i.Bytes())) - } else { - buf = make([]byte, 3) - buf[0] = 0 - buf[1] = uint8(len(i.Bytes()) >> 8) - buf[2] = uint8(len(i.Bytes())) - } - buf = append(buf, i.Bytes()...) - return buf -} - -// Set the public key for X and Y for Curve. The two -// values are just concatenated. -func curveToBuf(_X, _Y *big.Int, intlen int) []byte { - buf := intToBytes(_X, intlen) - buf = append(buf, intToBytes(_Y, intlen)...) - return buf -} - -// Set the public key for X and Y for Curve. The two -// values are just concatenated. -func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte { - t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8) - buf := []byte{byte(t)} - buf = append(buf, intToBytes(_Q, 20)...) - buf = append(buf, intToBytes(_P, 64+t*8)...) - buf = append(buf, intToBytes(_G, 64+t*8)...) - buf = append(buf, intToBytes(_Y, 64+t*8)...) - return buf -} - type wireSlice [][]byte func (p wireSlice) Len() int { return len(p) } @@ -676,6 +608,12 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { // NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, // HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, // SRV, DNAME, A6 + // + // RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC): + // Section 6.2 of [RFC4034] also erroneously lists HINFO as a record + // that needs conversion to lowercase, and twice at that. Since HINFO + // records contain no domain names, they are not subject to case + // conversion. switch x := r1.(type) { case *NS: x.Ns = strings.ToLower(x.Ns) @@ -716,41 +654,11 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { wires[i] = wire } sort.Sort(wires) - for _, wire := range wires { + for i, wire := range wires { + if i > 0 && bytes.Equal(wire, wires[i-1]) { + continue + } buf = append(buf, wire...) } return buf, nil } - -// 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) diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go new file mode 100644 index 00000000000..229a079370b --- /dev/null +++ b/vendor/github.com/miekg/dns/dnssec_keygen.go @@ -0,0 +1,156 @@ +package dns + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "math/big" +) + +// Generate generates a DNSKEY of the given bit size. +// The public part is put inside the DNSKEY record. +// The Algorithm in the key must be set as this will define +// what kind of DNSKEY will be generated. +// The ECDSA algorithms imply a fixed keysize, in that case +// bits should be set to the size of the algorithm. +func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) { + switch k.Algorithm { + case DSA, DSANSEC3SHA1: + if bits != 1024 { + return nil, ErrKeySize + } + case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1: + if bits < 512 || bits > 4096 { + return nil, ErrKeySize + } + case RSASHA512: + if bits < 1024 || bits > 4096 { + return nil, ErrKeySize + } + case ECDSAP256SHA256: + if bits != 256 { + return nil, ErrKeySize + } + case ECDSAP384SHA384: + if bits != 384 { + return nil, ErrKeySize + } + } + + switch k.Algorithm { + case DSA, DSANSEC3SHA1: + params := new(dsa.Parameters) + if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil { + return nil, err + } + priv := new(dsa.PrivateKey) + priv.PublicKey.Parameters = *params + err := dsa.GenerateKey(priv, rand.Reader) + if err != nil { + return nil, err + } + k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y) + return priv, nil + case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1: + priv, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, err + } + k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N) + return priv, nil + case ECDSAP256SHA256, ECDSAP384SHA384: + var c elliptic.Curve + switch k.Algorithm { + case ECDSAP256SHA256: + c = elliptic.P256() + case ECDSAP384SHA384: + c = elliptic.P384() + } + priv, err := ecdsa.GenerateKey(c, rand.Reader) + if err != nil { + return nil, err + } + k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y) + return priv, nil + default: + return nil, ErrAlg + } +} + +// Set the public key (the value E and N) +func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool { + if _E == 0 || _N == nil { + return false + } + buf := exponentToBuf(_E) + buf = append(buf, _N.Bytes()...) + k.PublicKey = toBase64(buf) + return true +} + +// Set the public key for Elliptic Curves +func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool { + if _X == nil || _Y == nil { + return false + } + var intlen int + switch k.Algorithm { + case ECDSAP256SHA256: + intlen = 32 + case ECDSAP384SHA384: + intlen = 48 + } + k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen)) + return true +} + +// Set the public key for DSA +func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool { + if _Q == nil || _P == nil || _G == nil || _Y == nil { + return false + } + buf := dsaToBuf(_Q, _P, _G, _Y) + k.PublicKey = toBase64(buf) + return true +} + +// Set the public key (the values E and N) for RSA +// RFC 3110: Section 2. RSA Public KEY Resource Records +func exponentToBuf(_E int) []byte { + var buf []byte + i := big.NewInt(int64(_E)) + if len(i.Bytes()) < 256 { + buf = make([]byte, 1) + buf[0] = uint8(len(i.Bytes())) + } else { + buf = make([]byte, 3) + buf[0] = 0 + buf[1] = uint8(len(i.Bytes()) >> 8) + buf[2] = uint8(len(i.Bytes())) + } + buf = append(buf, i.Bytes()...) + return buf +} + +// Set the public key for X and Y for Curve. The two +// values are just concatenated. +func curveToBuf(_X, _Y *big.Int, intlen int) []byte { + buf := intToBytes(_X, intlen) + buf = append(buf, intToBytes(_Y, intlen)...) + return buf +} + +// Set the public key for X and Y for Curve. The two +// values are just concatenated. +func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte { + t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8) + buf := []byte{byte(t)} + buf = append(buf, intToBytes(_Q, 20)...) + buf = append(buf, intToBytes(_P, 64+t*8)...) + buf = append(buf, intToBytes(_G, 64+t*8)...) + buf = append(buf, intToBytes(_Y, 64+t*8)...) + return buf +} diff --git a/vendor/github.com/miekg/dns/kscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go similarity index 73% rename from vendor/github.com/miekg/dns/kscan.go rename to vendor/github.com/miekg/dns/dnssec_keyscan.go index c48ca2d24b7..19a783389ab 100644 --- a/vendor/github.com/miekg/dns/kscan.go +++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go @@ -1,15 +1,19 @@ package dns import ( + "crypto" "crypto/dsa" "crypto/ecdsa" "crypto/rsa" "io" "math/big" + "strconv" "strings" ) -func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) { +// NewPrivateKey returns a PrivateKey by parsing the string s. +// s should be in the same form of the BIND private key files. +func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) { if s[len(s)-1] != '\n' { // We need a closing newline return k.ReadPrivateKey(strings.NewReader(s+"\n"), "") } @@ -18,9 +22,9 @@ func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) { // ReadPrivateKey reads a private key from the io.Reader q. The string file is // only used in error reporting. -// The public key must be -// known, because some cryptographic algorithms embed the public inside the privatekey. -func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) { +// The public key must be known, because some cryptographic algorithms embed +// the public inside the privatekey. +func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) { m, e := parseKey(q, file) if m == nil { return nil, e @@ -32,57 +36,63 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) { 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, ErrKey - } - 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, ErrKey - } - 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, ErrKey - } - return p, e + algo, err := strconv.Atoi(strings.SplitN(m["algorithm"], " ", 2)[0]) + if err != nil { + return nil, ErrPrivKey + } + switch uint8(algo) { + case DSA: + priv, e := readPrivateKeyDSA(m) + if e != nil { + return nil, e + } + pub := k.publicKeyDSA() + if pub == nil { + return nil, ErrKey + } + priv.PublicKey = *pub + return priv, e + case RSAMD5: + fallthrough + case RSASHA1: + fallthrough + case RSASHA1NSEC3SHA1: + fallthrough + case RSASHA256: + fallthrough + case RSASHA512: + priv, e := readPrivateKeyRSA(m) + if e != nil { + return nil, e + } + pub := k.publicKeyRSA() + if pub == nil { + return nil, ErrKey + } + priv.PublicKey = *pub + return priv, e + case ECCGOST: + return nil, ErrPrivKey + case ECDSAP256SHA256: + fallthrough + case ECDSAP384SHA384: + priv, e := readPrivateKeyECDSA(m) + if e != nil { + return nil, e + } + pub := k.publicKeyECDSA() + if pub == nil { + return nil, ErrKey + } + priv.PublicKey = *pub + return priv, e + default: + return nil, ErrPrivKey } - 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) { +func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) { p := new(rsa.PrivateKey) p.Primes = []*big.Int{nil, nil} for k, v := range m { @@ -119,7 +129,7 @@ func readPrivateKeyRSA(m map[string]string) (PrivateKey, error) { return p, nil } -func readPrivateKeyDSA(m map[string]string) (PrivateKey, error) { +func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) { p := new(dsa.PrivateKey) p.X = big.NewInt(0) for k, v := range m { @@ -137,7 +147,7 @@ func readPrivateKeyDSA(m map[string]string) (PrivateKey, error) { return p, nil } -func readPrivateKeyECDSA(m map[string]string) (PrivateKey, error) { +func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) { p := new(ecdsa.PrivateKey) p.D = big.NewInt(0) // TODO: validate that the required flags are present @@ -156,11 +166,6 @@ func readPrivateKeyECDSA(m map[string]string) (PrivateKey, error) { return p, nil } -func readPrivateKeyGOST(m map[string]string) (PrivateKey, error) { - // TODO(miek) - 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) { @@ -173,9 +178,9 @@ func parseKey(r io.Reader, file string) (map[string]string, error) { for l := range c { // It should alternate switch l.value { - case _KEY: + case zKey: k = l.token - case _VALUE: + case zValue: if k == "" { return nil, &ParseError{file, "no private key seen", l} } @@ -205,14 +210,14 @@ func klexer(s *scan, c chan lex) { } l.token = str if key { - l.value = _KEY + l.value = zKey c <- l // Next token is a space, eat it s.tokenText() key = false str = "" } else { - l.value = _VALUE + l.value = zValue } case ';': commt = true @@ -221,7 +226,7 @@ func klexer(s *scan, c chan lex) { // Reset a comment commt = false } - l.value = _VALUE + l.value = zValue l.token = str c <- l str = "" @@ -238,7 +243,7 @@ func klexer(s *scan, c chan lex) { if len(str) > 0 { // Send remainder l.token = str - l.value = _VALUE + l.value = zValue c <- l } } diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go new file mode 100644 index 00000000000..56f3ea934f6 --- /dev/null +++ b/vendor/github.com/miekg/dns/dnssec_privkey.go @@ -0,0 +1,85 @@ +package dns + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "math/big" + "strconv" +) + +const format = "Private-key-format: v1.3\n" + +// PrivateKeyString converts a PrivateKey to a string. This string has the same +// format as the private-key-file of BIND9 (Private-key-format: v1.3). +// It needs some info from the key (the algorithm), so its a method of the DNSKEY +// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey +func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string { + algorithm := strconv.Itoa(int(r.Algorithm)) + algorithm += " (" + AlgorithmToString[r.Algorithm] + ")" + + switch p := p.(type) { + case *rsa.PrivateKey: + modulus := toBase64(p.PublicKey.N.Bytes()) + e := big.NewInt(int64(p.PublicKey.E)) + publicExponent := toBase64(e.Bytes()) + privateExponent := toBase64(p.D.Bytes()) + prime1 := toBase64(p.Primes[0].Bytes()) + prime2 := toBase64(p.Primes[1].Bytes()) + // Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm + // and from: http://code.google.com/p/go/issues/detail?id=987 + one := big.NewInt(1) + p1 := big.NewInt(0).Sub(p.Primes[0], one) + q1 := big.NewInt(0).Sub(p.Primes[1], one) + exp1 := big.NewInt(0).Mod(p.D, p1) + exp2 := big.NewInt(0).Mod(p.D, q1) + coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0]) + + exponent1 := toBase64(exp1.Bytes()) + exponent2 := toBase64(exp2.Bytes()) + coefficient := toBase64(coeff.Bytes()) + + return format + + "Algorithm: " + algorithm + "\n" + + "Modulus: " + modulus + "\n" + + "PublicExponent: " + publicExponent + "\n" + + "PrivateExponent: " + privateExponent + "\n" + + "Prime1: " + prime1 + "\n" + + "Prime2: " + prime2 + "\n" + + "Exponent1: " + exponent1 + "\n" + + "Exponent2: " + exponent2 + "\n" + + "Coefficient: " + coefficient + "\n" + + case *ecdsa.PrivateKey: + var intlen int + switch r.Algorithm { + case ECDSAP256SHA256: + intlen = 32 + case ECDSAP384SHA384: + intlen = 48 + } + private := toBase64(intToBytes(p.D, intlen)) + return format + + "Algorithm: " + algorithm + "\n" + + "PrivateKey: " + private + "\n" + + case *dsa.PrivateKey: + T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8) + prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8)) + subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20)) + base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8)) + priv := toBase64(intToBytes(p.X, 20)) + pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8)) + return format + + "Algorithm: " + algorithm + "\n" + + "Prime(p): " + prime + "\n" + + "Subprime(q): " + subprime + "\n" + + "Base(g): " + base + "\n" + + "Private_value(x): " + priv + "\n" + + "Public_value(y): " + pub + "\n" + + default: + return "" + } +} diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go new file mode 100644 index 00000000000..f3555e43399 --- /dev/null +++ b/vendor/github.com/miekg/dns/doc.go @@ -0,0 +1,251 @@ +/* +Package dns implements a full featured interface to the Domain Name System. +Server- and client-side programming is supported. +The package allows complete control over what is send out to the DNS. The package +API follows the less-is-more principle, by presenting a small, clean interface. + +The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers, +TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing. +Note that domain names MUST be fully qualified, before sending them, unqualified +names in a message will result in a packing failure. + +Resource records are native types. They are not stored in wire format. +Basic usage pattern for creating a new resource record: + + r := new(dns.MX) + r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, + Class: dns.ClassINET, Ttl: 3600} + r.Preference = 10 + r.Mx = "mx.miek.nl." + +Or directly from a string: + + mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") + +Or when the default TTL (3600) and class (IN) suit you: + + mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.") + +Or even: + + mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") + +In the DNS messages are exchanged, these messages contain resource +records (sets). Use pattern for creating a message: + + m := new(dns.Msg) + m.SetQuestion("miek.nl.", dns.TypeMX) + +Or when not certain if the domain name is fully qualified: + + m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX) + +The message m is now a message with the question section set to ask +the MX records for the miek.nl. zone. + +The following is slightly more verbose, but more flexible: + + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.RecursionDesired = true + m1.Question = make([]dns.Question, 1) + m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} + +After creating a message it can be send. +Basic use pattern for synchronous querying the DNS at a +server configured on 127.0.0.1 and port 53: + + c := new(dns.Client) + in, rtt, err := c.Exchange(m1, "127.0.0.1:53") + +Suppressing multiple outstanding queries (with the same question, type and +class) is as easy as setting: + + c.SingleInflight = true + +If these "advanced" features are not needed, a simple UDP query can be send, +with: + + in, err := dns.Exchange(m1, "127.0.0.1:53") + +When this functions returns you will get dns message. A dns message consists +out of four sections. +The question section: in.Question, the answer section: in.Answer, +the authority section: in.Ns and the additional section: in.Extra. + +Each of these sections (except the Question section) contain a []RR. Basic +use pattern for accessing the rdata of a TXT RR as the first RR in +the Answer section: + + if t, ok := in.Answer[0].(*dns.TXT); ok { + // do something with t.Txt + } + +Domain Name and TXT Character String Representations + +Both domain names and TXT character strings are converted to presentation +form both when unpacked and when converted to strings. + +For TXT character strings, tabs, carriage returns and line feeds will be +converted to \t, \r and \n respectively. Back slashes and quotations marks +will be escaped. Bytes below 32 and above 127 will be converted to \DDD +form. + +For domain names, in addition to the above rules brackets, periods, +spaces, semicolons and the at symbol are escaped. + +DNSSEC + +DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It +uses public key cryptography to sign resource records. The +public keys are stored in DNSKEY records and the signatures in RRSIG records. + +Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit +to a request. + + m := new(dns.Msg) + m.SetEdns0(4096, true) + +Signature generation, signature verification and key generation are all supported. + +DYNAMIC UPDATES + +Dynamic updates reuses the DNS message format, but renames three of +the sections. Question is Zone, Answer is Prerequisite, Authority is +Update, only the Additional is not renamed. See RFC 2136 for the gory details. + +You can set a rather complex set of rules for the existence of absence of +certain resource records or names in a zone to specify if resource records +should be added or removed. The table from RFC 2136 supplemented with the Go +DNS function shows which functions exist to specify the prerequisites. + + 3.2.4 - Table Of Metavalues Used In Prerequisite Section + + CLASS TYPE RDATA Meaning Function + -------------------------------------------------------------- + ANY ANY empty Name is in use dns.NameUsed + ANY rrset empty RRset exists (value indep) dns.RRsetUsed + NONE ANY empty Name is not in use dns.NameNotUsed + NONE rrset empty RRset does not exist dns.RRsetNotUsed + zone rrset rr RRset exists (value dep) dns.Used + +The prerequisite section can also be left empty. +If you have decided on the prerequisites you can tell what RRs should +be added or deleted. The next table shows the options you have and +what functions to call. + + 3.4.2.6 - Table Of Metavalues Used In Update Section + + CLASS TYPE RDATA Meaning Function + --------------------------------------------------------------- + ANY ANY empty Delete all RRsets from name dns.RemoveName + ANY rrset empty Delete an RRset dns.RemoveRRset + NONE rrset rr Delete an RR from RRset dns.Remove + zone rrset rr Add to an RRset dns.Insert + +TRANSACTION SIGNATURE + +An TSIG or transaction signature adds a HMAC TSIG record to each message sent. +The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512. + +Basic use pattern when querying with a TSIG name "axfr." (note that these key names +must be fully qualified - as they are domain names) and the base64 secret +"so6ZGir4GPAqINNh9U5c3A==": + + c := new(dns.Client) + c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} + m := new(dns.Msg) + m.SetQuestion("miek.nl.", dns.TypeMX) + m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) + ... + // When sending the TSIG RR is calculated and filled in before sending + +When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with +TSIG, this is the basic use pattern. In this example we request an AXFR for +miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A==" +and using the server 176.58.119.54: + + t := new(dns.Transfer) + m := new(dns.Msg) + t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} + m.SetAxfr("miek.nl.") + m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) + c, err := t.In(m, "176.58.119.54:53") + for r := range c { ... } + +You can now read the records from the transfer as they come in. Each envelope is checked with TSIG. +If something is not correct an error is returned. + +Basic use pattern validating and replying to a message that has TSIG set. + + server := &dns.Server{Addr: ":53", Net: "udp"} + server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} + go server.ListenAndServe() + dns.HandleFunc(".", handleRequest) + + func handleRequest(w dns.ResponseWriter, r *dns.Msg) { + m := new(dns.Msg) + m.SetReply(r) + if r.IsTsig() != nil { + if w.TsigStatus() == nil { + // *Msg r has an TSIG record and it was validated + m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) + } else { + // *Msg r has an TSIG records and it was not valided + } + } + w.WriteMsg(m) + } + +PRIVATE RRS + +RFC 6895 sets aside a range of type codes for private use. This range +is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these +can be used, before requesting an official type code from IANA. + +see http://miek.nl/posts/2014/Sep/21/Private%20RRs%20and%20IDN%20in%20Go%20DNS/ for more +information. + +EDNS0 + +EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated +by RFC 6891. It defines an new RR type, the OPT RR, which is then completely +abused. +Basic use pattern for creating an (empty) OPT RR: + + o := new(dns.OPT) + o.Hdr.Name = "." // MUST be the root zone, per definition. + o.Hdr.Rrtype = dns.TypeOPT + +The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) +interfaces. Currently only a few have been standardized: EDNS0_NSID +(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note +that these options may be combined in an OPT RR. +Basic use pattern for a server to check if (and which) options are set: + + // o is a dns.OPT + for _, s := range o.Option { + switch e := s.(type) { + case *dns.EDNS0_NSID: + // do stuff with e.Nsid + case *dns.EDNS0_SUBNET: + // access e.Family, e.Address, etc. + } + } + +SIG(0) + +From RFC 2931: + + SIG(0) provides protection for DNS transactions and requests .... + ... protection for glue records, DNS requests, protection for message headers + on requests and responses, and protection of the overall integrity of a response. + +It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared +secret approach in TSIG. +Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and +RSASHA512. + +Signing subsequent messages in multi-message sessions is not implemented. +*/ +package dns diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 8b676e61241..0c47f6ea5a4 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -1,29 +1,3 @@ -// EDNS0 -// -// EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated -// by RFC 6891. It defines an new RR type, the OPT RR, which is then completely -// abused. -// Basic use pattern for creating an (empty) OPT RR: -// -// o := new(dns.OPT) -// o.Hdr.Name = "." // MUST be the root zone, per definition. -// o.Hdr.Rrtype = dns.TypeOPT -// -// The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) -// interfaces. Currently only a few have been standardized: EDNS0_NSID -// (RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note -// that these options may be combined in an OPT RR. -// Basic use pattern for a server to check if (and which) options are set: -// -// // o is a dns.OPT -// for _, s := range o.Option { -// switch e := s.(type) { -// case *dns.EDNS0_NSID: -// // do stuff with e.Nsid -// case *dns.EDNS0_SUBNET: -// // access e.Family, e.Address, etc. -// } -// } package dns import ( @@ -44,18 +18,18 @@ const ( EDNS0SUBNET = 0x8 // client-subnet (RFC6891) EDNS0EXPIRE = 0x9 // EDNS0 expire EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET + EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891) + EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891) _DO = 1 << 15 // dnssec ok ) +// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. +// See RFC 6891. type OPT struct { Hdr RR_Header Option []EDNS0 `dns:"opt"` } -func (rr *OPT) 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() { @@ -92,6 +66,8 @@ func (rr *OPT) String() string { s += "\n; DS HASH UNDERSTOOD: " + o.String() case *EDNS0_N3U: s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String() + case *EDNS0_LOCAL: + s += "\n; LOCAL OPT: " + o.String() } } return s @@ -100,16 +76,13 @@ func (rr *OPT) String() string { func (rr *OPT) len() int { l := rr.Hdr.len() for i := 0; i < len(rr.Option); i++ { + l += 4 // Account for 2-byte option code and 2-byte option length. lo, _ := rr.Option[i].pack() - l += 2 + len(lo) + l += 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. @@ -195,7 +168,7 @@ func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil } func (e *EDNS0_NSID) String() string { return string(e.Nsid) } -// The subnet EDNS0 option is used to give the remote nameserver +// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver // an idea of where the client lives. It can then give back a different // answer depending on the location or network topology. // Basic use pattern for creating an subnet option: @@ -211,6 +184,11 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) } // e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 // // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 // o.Option = append(o.Option, e) +// +// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic +// for which netmask applies to the address. This code will parse all the +// available bits when unpacking (up to optlen). When packing it will apply +// SourceNetmask. If you need more advanced logic, patches welcome and good luck. type EDNS0_SUBNET struct { Code uint16 // Always EDNS0SUBNET Family uint16 // 1 for IP, 2 for IP6 @@ -237,38 +215,22 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) { 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] + if len(e.Address.To4()) != net.IPv4len { + return nil, errors.New("dns: bad address") } - needLength := e.SourceNetmask / 8 - if e.SourceNetmask%8 > 0 { - needLength++ - } - ip = ip[:needLength] - b = append(b, ip...) + ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) + needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up + b = append(b, ip[:needLength]...) case 2: if e.SourceNetmask > net.IPv6len*8 { return nil, errors.New("dns: bad netmask") } - 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] + if len(e.Address) != net.IPv6len { + return nil, errors.New("dns: bad address") } - needLength := e.SourceNetmask / 8 - if e.SourceNetmask%8 > 0 { - needLength++ - } - ip = ip[:needLength] - b = append(b, ip...) + ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) + needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up + b = append(b, ip[:needLength]...) default: return nil, errors.New("dns: bad address family") } @@ -276,8 +238,7 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) { } func (e *EDNS0_SUBNET) unpack(b []byte) error { - lb := len(b) - if lb < 4 { + if len(b) < 4 { return ErrBuf } e.Family, _ = unpackUint16(b, 0) @@ -285,25 +246,27 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error { e.SourceScope = b[3] switch e.Family { case 1: - addr := make([]byte, 4) - for i := 0; i < int(e.SourceNetmask/8); i++ { - if i >= len(addr) || 4+i >= len(b) { - return ErrBuf - } + if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 { + return errors.New("dns: bad netmask") + } + addr := make([]byte, net.IPv4len) + for i := 0; i < net.IPv4len && 4+i < len(b); i++ { addr[i] = b[4+i] } e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3]) case 2: - addr := make([]byte, 16) - for i := 0; i < int(e.SourceNetmask/8); i++ { - if i >= len(addr) || 4+i >= len(b) { - return ErrBuf - } + if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 { + return errors.New("dns: bad netmask") + } + addr := make([]byte, net.IPv6len) + for i := 0; i < net.IPv6len && 4+i < len(b); i++ { addr[i] = b[4+i] } e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]} + default: + return errors.New("dns: bad address family") } return nil } @@ -320,7 +283,7 @@ func (e *EDNS0_SUBNET) String() (s string) { return } -// The UL (Update Lease) EDNS0 (draft RFC) option is used to tell the server to set +// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set // an expiration on an update RR. This is helpful for clients that cannot clean // up after themselves. This is a draft RFC and more information can be found at // http://files.dns-sd.org/draft-sekar-dns-ul.txt @@ -358,7 +321,7 @@ func (e *EDNS0_UL) unpack(b []byte) error { return nil } -// Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 +// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 // Implemented for completeness, as the EDNS0 type code is assigned. type EDNS0_LLQ struct { Code uint16 // Always EDNS0LLQ @@ -499,3 +462,44 @@ func (e *EDNS0_EXPIRE) unpack(b []byte) error { e.Expire = uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) return nil } + +// The EDNS0_LOCAL option is used for local/experimental purposes. The option +// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND] +// (RFC6891), although any unassigned code can actually be used. The content of +// the option is made available in Data, unaltered. +// Basic use pattern for creating a local option: +// +// o := new(dns.OPT) +// o.Hdr.Name = "." +// o.Hdr.Rrtype = dns.TypeOPT +// e := new(dns.EDNS0_LOCAL) +// e.Code = dns.EDNS0LOCALSTART +// e.Data = []byte{72, 82, 74} +// o.Option = append(o.Option, e) +type EDNS0_LOCAL struct { + Code uint16 + Data []byte +} + +func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } +func (e *EDNS0_LOCAL) String() string { + return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) +} + +func (e *EDNS0_LOCAL) pack() ([]byte, error) { + b := make([]byte, len(e.Data)) + copied := copy(b, e.Data) + if copied != len(e.Data) { + return nil, ErrBuf + } + return b, nil +} + +func (e *EDNS0_LOCAL) unpack(b []byte) error { + e.Data = make([]byte, len(b)) + copied := copy(e.Data, b) + if copied != len(b) { + return ErrBuf + } + return nil +} diff --git a/vendor/github.com/miekg/dns/format.go b/vendor/github.com/miekg/dns/format.go new file mode 100644 index 00000000000..1ac1664fe29 --- /dev/null +++ b/vendor/github.com/miekg/dns/format.go @@ -0,0 +1,96 @@ +package dns + +import ( + "net" + "reflect" + "strconv" +) + +// NumField returns the number of rdata fields r has. +func NumField(r RR) int { + return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header +} + +// Field returns the rdata field i as a string. Fields are indexed starting from 1. +// RR types that holds slice data, for instance the NSEC type bitmap will return a single +// string where the types are concatenated using a space. +// Accessing non existing fields will cause a panic. +func Field(r RR, i int) string { + if i == 0 { + return "" + } + d := reflect.ValueOf(r).Elem().Field(i) + switch k := d.Kind(); k { + case reflect.String: + return d.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(d.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(d.Uint(), 10) + case reflect.Slice: + switch reflect.ValueOf(r).Elem().Type().Field(i).Tag { + case `dns:"a"`: + // TODO(miek): Hmm store this as 16 bytes + if d.Len() < net.IPv6len { + return net.IPv4(byte(d.Index(0).Uint()), + byte(d.Index(1).Uint()), + byte(d.Index(2).Uint()), + byte(d.Index(3).Uint())).String() + } + return net.IPv4(byte(d.Index(12).Uint()), + byte(d.Index(13).Uint()), + byte(d.Index(14).Uint()), + byte(d.Index(15).Uint())).String() + case `dns:"aaaa"`: + return net.IP{ + byte(d.Index(0).Uint()), + byte(d.Index(1).Uint()), + byte(d.Index(2).Uint()), + byte(d.Index(3).Uint()), + byte(d.Index(4).Uint()), + byte(d.Index(5).Uint()), + byte(d.Index(6).Uint()), + byte(d.Index(7).Uint()), + byte(d.Index(8).Uint()), + byte(d.Index(9).Uint()), + byte(d.Index(10).Uint()), + byte(d.Index(11).Uint()), + byte(d.Index(12).Uint()), + byte(d.Index(13).Uint()), + byte(d.Index(14).Uint()), + byte(d.Index(15).Uint()), + }.String() + case `dns:"nsec"`: + if d.Len() == 0 { + return "" + } + s := Type(d.Index(0).Uint()).String() + for i := 1; i < d.Len(); i++ { + s += " " + Type(d.Index(i).Uint()).String() + } + return s + case `dns:"wks"`: + if d.Len() == 0 { + return "" + } + s := strconv.Itoa(int(d.Index(0).Uint())) + for i := 0; i < d.Len(); i++ { + s += " " + strconv.Itoa(int(d.Index(i).Uint())) + } + return s + default: + // if it does not have a tag its a string slice + fallthrough + case `dns:"txt"`: + if d.Len() == 0 { + return "" + } + s := d.Index(0).String() + for i := 1; i < d.Len(); i++ { + s += " " + d.Index(i).String() + } + return s + } + } + return "" +} diff --git a/vendor/github.com/miekg/dns/keygen.go b/vendor/github.com/miekg/dns/keygen.go deleted file mode 100644 index dfe328ecd43..00000000000 --- a/vendor/github.com/miekg/dns/keygen.go +++ /dev/null @@ -1,157 +0,0 @@ -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 := toBase64(t.PublicKey.N.Bytes()) - e := big.NewInt(int64(t.PublicKey.E)) - publicExponent := toBase64(e.Bytes()) - privateExponent := toBase64(t.D.Bytes()) - prime1 := toBase64(t.Primes[0].Bytes()) - prime2 := toBase64(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 := toBase64(exp1.Bytes()) - exponent2 := toBase64(exp2.Bytes()) - coefficient := toBase64(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] + ")" - var intlen int - switch r.Algorithm { - case ECDSAP256SHA256: - intlen = 32 - case ECDSAP384SHA384: - intlen = 48 - } - private := toBase64(intToBytes(t.D, intlen)) - s = _FORMAT + - "Algorithm: " + algorithm + "\n" + - "PrivateKey: " + private + "\n" - case *dsa.PrivateKey: - algorithm := strconv.Itoa(int(r.Algorithm)) + " (" + AlgorithmToString[r.Algorithm] + ")" - T := divRoundUp(divRoundUp(t.PublicKey.Parameters.G.BitLen(), 8)-64, 8) - prime := toBase64(intToBytes(t.PublicKey.Parameters.P, 64+T*8)) - subprime := toBase64(intToBytes(t.PublicKey.Parameters.Q, 20)) - base := toBase64(intToBytes(t.PublicKey.Parameters.G, 64+T*8)) - priv := toBase64(intToBytes(t.X, 20)) - pub := toBase64(intToBytes(t.PublicKey.Y, 64+T*8)) - 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 -} diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index 758e5783de5..cb549fc672c 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -4,9 +4,11 @@ package dns // SplitDomainName splits a name string into it's labels. // www.miek.nl. returns []string{"www", "miek", "nl"} +// .www.miek.nl. returns []string{"", "www", "miek", "nl"}, // The root label (.) returns nil. Note that using // strings.Split(s) will work in most cases, but does not handle // escaped dots (\.) for instance. +// s must be a syntactically valid domain name, see IsDomainName. func SplitDomainName(s string) (labels []string) { if len(s) == 0 { return nil @@ -45,6 +47,8 @@ func SplitDomainName(s string) (labels []string) { // // www.miek.nl. and miek.nl. have two labels in common: miek and nl // www.miek.nl. and www.bla.nl. have one label in common: nl +// +// s1 and s2 must be syntactically valid domain names. func CompareDomainName(s1, s2 string) (n int) { s1 = Fqdn(s1) s2 = Fqdn(s2) @@ -85,6 +89,7 @@ func CompareDomainName(s1, s2 string) (n int) { } // CountLabel counts the the number of labels in the string s. +// s must be a syntactically valid domain name. func CountLabel(s string) (labels int) { if s == "." { return @@ -98,12 +103,12 @@ func CountLabel(s string) (labels int) { 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. Also see dns.SplitDomainName. +// The root name (.) returns nil. Also see SplitDomainName. +// s must be a syntactically valid domain name. func Split(s string) []int { if s == "." { return nil @@ -119,12 +124,12 @@ func Split(s string) []int { } 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. +// Also see PrevLabel. func NextLabel(s string, offset int) (i int, end bool) { quote := false for i = offset; i < len(s)-1; i++ { @@ -147,6 +152,7 @@ func NextLabel(s string, offset int) (i int, end bool) { // PrevLabel returns the index of the label when starting from the right and // jumping n labels to the left. // The bool start is true when the start of the string has been overshot. +// Also see NextLabel. func PrevLabel(s string, n int) (i int, start bool) { if n == 0 { return len(s), false diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index ecba463ddda..7274800fc01 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -23,29 +23,40 @@ import ( const maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer var ( - ErrAlg error = &Error{err: "bad algorithm"} - ErrAuth error = &Error{err: "bad authentication"} - ErrBuf error = &Error{err: "buffer size too small"} - ErrConnEmpty error = &Error{err: "conn has no connection"} - ErrConn error = &Error{err: "conn holds both UDP and TCP connection"} + // ErrAlg indicates an error with the (DNSSEC) algorithm. + ErrAlg error = &Error{err: "bad algorithm"} + // ErrAuth indicates an error in the TSIG authentication. + ErrAuth error = &Error{err: "bad authentication"} + // ErrBuf indicates that the buffer used it too small for the message. + ErrBuf error = &Error{err: "buffer size too small"} + // ErrConnEmpty indicates a connection is being uses before it is initialized. + ErrConnEmpty error = &Error{err: "conn has no connection"} + // ErrExtendedRcode ... ErrExtendedRcode error = &Error{err: "bad extended rcode"} - ErrFqdn error = &Error{err: "domain must be fully qualified"} - ErrId error = &Error{err: "id mismatch"} - ErrKeyAlg error = &Error{err: "bad key algorithm"} - ErrKey error = &Error{err: "bad key"} - ErrKeySize error = &Error{err: "bad key size"} - ErrNoSig error = &Error{err: "no signature found"} - ErrPrivKey error = &Error{err: "bad private key"} - ErrRcode error = &Error{err: "bad rcode"} - ErrRdata error = &Error{err: "bad rdata"} - ErrRRset error = &Error{err: "bad rrset"} - ErrSecret error = &Error{err: "no secrets defined"} - ErrServ error = &Error{err: "no servers could be reached"} - ErrShortRead error = &Error{err: "short read"} - ErrSig error = &Error{err: "bad signature"} - ErrSigGen error = &Error{err: "bad signature generation"} - ErrSoa error = &Error{err: "no SOA"} - ErrTime error = &Error{err: "bad time"} + // ErrFqdn indicates that a domain name does not have a closing dot. + ErrFqdn error = &Error{err: "domain must be fully qualified"} + // ErrId indicates there is a mismatch with the message's ID. + ErrId error = &Error{err: "id mismatch"} + // ErrKeyAlg indicates that the algorithm in the key is not valid. + ErrKeyAlg error = &Error{err: "bad key algorithm"} + ErrKey error = &Error{err: "bad key"} + ErrKeySize error = &Error{err: "bad key size"} + ErrNoSig error = &Error{err: "no signature found"} + ErrPrivKey error = &Error{err: "bad private key"} + ErrRcode error = &Error{err: "bad rcode"} + ErrRdata error = &Error{err: "bad rdata"} + ErrRRset error = &Error{err: "bad rrset"} + ErrSecret error = &Error{err: "no secrets defined"} + ErrShortRead error = &Error{err: "short read"} + // ErrSig indicates that a signature can not be cryptographically validated. + ErrSig error = &Error{err: "bad signature"} + // ErrSOA indicates that no SOA RR was seen when doing zone transfers. + ErrSoa error = &Error{err: "no SOA"} + // ErrTime indicates a timing error in TSIG authentication. + ErrTime error = &Error{err: "bad time"} + // ErrTruncated indicates that we failed to unpack a truncated message. + // We unpacked as much as we had so Msg can still be used, if desired. + ErrTruncated error = &Error{err: "failed to unpack truncated message"} ) // Id, by default, returns a 16 bits random number to be used as a @@ -56,8 +67,7 @@ var ( // dns.Id = func() uint16 { return 3 } var Id func() uint16 = id -// A manually-unpacked version of (id, bits). -// This is in its own struct for easy printing. +// MsgHdr is a a manually-unpacked version of (id, bits). type MsgHdr struct { Id uint16 Response bool @@ -72,7 +82,7 @@ type MsgHdr struct { Rcode int } -// The layout of a DNS message. +// Msg contains the layout of a DNS message. type Msg struct { MsgHdr Compress bool `json:"-"` // If true, the message will be compressed when converted to wire format. This not part of the official DNS packet format. @@ -82,87 +92,10 @@ type Msg struct { Extra []RR // Holds the RR(s) of the additional section. } -// Map of strings for each RR wire type. -var TypeToString = map[uint16]string{ - TypeA: "A", - TypeAAAA: "AAAA", - TypeAFSDB: "AFSDB", - TypeANY: "ANY", // Meta RR - TypeATMA: "ATMA", - TypeAXFR: "AXFR", // Meta RR - TypeCAA: "CAA", - TypeCDNSKEY: "CDNSKEY", - TypeCDS: "CDS", - TypeCERT: "CERT", - TypeCNAME: "CNAME", - TypeDHCID: "DHCID", - TypeDLV: "DLV", - TypeDNAME: "DNAME", - TypeDNSKEY: "DNSKEY", - TypeDS: "DS", - TypeEID: "EID", - TypeEUI48: "EUI48", - TypeEUI64: "EUI64", - TypeGID: "GID", - TypeGPOS: "GPOS", - TypeHINFO: "HINFO", - TypeHIP: "HIP", - TypeIPSECKEY: "IPSECKEY", - TypeISDN: "ISDN", - TypeIXFR: "IXFR", // Meta RR - TypeKEY: "KEY", - TypeKX: "KX", - TypeL32: "L32", - TypeL64: "L64", - TypeLOC: "LOC", - TypeLP: "LP", - TypeMB: "MB", - TypeMD: "MD", - TypeMF: "MF", - TypeMG: "MG", - TypeMINFO: "MINFO", - TypeMR: "MR", - TypeMX: "MX", - TypeNAPTR: "NAPTR", - TypeNID: "NID", - TypeNINFO: "NINFO", - TypeNIMLOC: "NIMLOC", - TypeNS: "NS", - TypeNSAP: "NSAP", - TypeNSAPPTR: "NSAP-PTR", - TypeNSEC3: "NSEC3", - TypeNSEC3PARAM: "NSEC3PARAM", - TypeNSEC: "NSEC", - TypeNULL: "NULL", - TypeOPT: "OPT", - TypeOPENPGPKEY: "OPENPGPKEY", - TypePTR: "PTR", - TypeRKEY: "RKEY", - TypeRP: "RP", - TypeRRSIG: "RRSIG", - TypeRT: "RT", - TypeSIG: "SIG", - TypeSOA: "SOA", - TypeSPF: "SPF", - TypeSRV: "SRV", - TypeSSHFP: "SSHFP", - TypeTA: "TA", - TypeTALINK: "TALINK", - TypeTKEY: "TKEY", // Meta RR - TypeTLSA: "TLSA", - TypeTSIG: "TSIG", // Meta RR - TypeTXT: "TXT", - TypePX: "PX", - TypeUID: "UID", - TypeUINFO: "UINFO", - TypeUNSPEC: "UNSPEC", - TypeURI: "URI", - TypeWKS: "WKS", - TypeX25: "X25", -} - -// Reverse, needed for string parsing. +// StringToType is the reverse of TypeToString, needed for string parsing. var StringToType = reverseInt16(TypeToString) + +// StringToClass is the reverse of ClassToString, needed for string parsing. var StringToClass = reverseInt16(ClassToString) // Map of opcodes strings. @@ -171,7 +104,7 @@ var StringToOpcode = reverseInt(OpcodeToString) // Map of rcodes strings. var StringToRcode = reverseInt(RcodeToString) -// Map of strings for each CLASS wire type. +// ClassToString is a maps Classes to strings for each CLASS wire type. var ClassToString = map[uint16]string{ ClassINET: "IN", ClassCSNET: "CS", @@ -181,7 +114,7 @@ var ClassToString = map[uint16]string{ ClassANY: "ANY", } -// Map of strings for opcodes. +// OpcodeToString maps Opcodes to strings. var OpcodeToString = map[int]string{ OpcodeQuery: "QUERY", OpcodeIQuery: "IQUERY", @@ -190,7 +123,7 @@ var OpcodeToString = map[int]string{ OpcodeUpdate: "UPDATE", } -// Map of strings for rcodes. +// RcodeToString maps Rcodes to strings. var RcodeToString = map[int]string{ RcodeSuccess: "NOERROR", RcodeFormatError: "FORMERR", @@ -225,7 +158,7 @@ var RcodeToString = map[int]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[]. +// 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 @@ -264,7 +197,7 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c // Emit sequence of counted strings, chopping at dots. begin := 0 bs := []byte(s) - ro_bs, bs_fresh, escaped_dot := s, true, false + roBs, bsFresh, escapedDot := s, true, false for i := 0; i < ls; i++ { if bs[i] == '\\' { for j := i; j < ls-1; j++ { @@ -288,13 +221,13 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c } else if bs[i] == 'n' { bs[i] = '\n' } - escaped_dot = bs[i] == '.' - bs_fresh = false + escapedDot = bs[i] == '.' + bsFresh = false continue } if bs[i] == '.' { - if i > 0 && bs[i-1] == '.' && !escaped_dot { + if i > 0 && bs[i-1] == '.' && !escapedDot { // two dots back to back is not legal return lenmsg, labels, ErrRdata } @@ -320,16 +253,16 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c } off++ } - if compress && !bs_fresh { - ro_bs = string(bs) - bs_fresh = true + if compress && !bsFresh { + roBs = string(bs) + bsFresh = true } - // Dont try to compress '.' - if compress && ro_bs[begin:] != "." { - if p, ok := compression[ro_bs[begin:]]; !ok { + // Don't try to compress '.' + if compress && roBs[begin:] != "." { + if p, ok := compression[roBs[begin:]]; !ok { // Only offsets smaller than this can be used. if offset < maxCompressionOffset { - compression[ro_bs[begin:]] = offset + compression[roBs[begin:]] = offset } } else { // The first hit is the longest matching dname @@ -348,7 +281,7 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c labels++ begin = i + 1 } - escaped_dot = false + escapedDot = false } // Root label is special if len(bs) == 1 && bs[0] == '.' { @@ -401,9 +334,6 @@ Loop: case 0x00: if c == 0x00 { // end of name - if len(s) == 0 { - return ".", off, nil - } break Loop } // literal string @@ -464,6 +394,9 @@ Loop: if ptr == 0 { off1 = off } + if len(s) == 0 { + s = []byte(".") + } return string(s), off1, nil } @@ -531,17 +464,46 @@ func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { return offset, nil } -func unpackTxt(msg []byte, offset, rdend int) ([]string, int, error) { - var err error - var ss []string +func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) { + if offset >= len(msg) { + return offset, ErrBuf + } + bs := tmp[:len(s)] + copy(bs, s) + for i := 0; i < len(bs); i++ { + if len(msg) <= offset { + return offset, ErrBuf + } + if bs[i] == '\\' { + i++ + if i == len(bs) { + break + } + // check for \DDD + if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { + msg[offset] = dddToByte(bs[i:]) + i += 2 + } else { + msg[offset] = bs[i] + } + } else { + msg[offset] = bs[i] + } + offset++ + } + return offset, nil +} + +func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { + off = off0 var s string - for offset < rdend && err == nil { - s, offset, err = unpackTxtString(msg, offset) + for off < len(msg) && err == nil { + s, off, err = unpackTxtString(msg, off) if err == nil { ss = append(ss, s) } } - return ss, offset, err + return } func unpackTxtString(msg []byte, offset int) (string, int, error) { @@ -652,6 +614,12 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str off += len(b) } case `dns:"a"`: + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, must be 1 + if val.Field(2).Uint() != 1 { + continue + } + } // It must be a slice of 4, even if it is 16, we encode // only the first 4 if off+net.IPv4len > lenmsg { @@ -676,6 +644,12 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str return lenmsg, &Error{err: "overflow packing a"} } case `dns:"aaaa"`: + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, must be 2 + if val.Field(2).Uint() != 2 { + continue + } + } if fv.Len() == 0 { break } @@ -694,58 +668,49 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str if val.Field(i).Len() == 0 { break } - var bitmapbyte uint16 + off1 := off 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"} + serv := int(fv.Index(j).Uint()) + if off+serv/8+1 > len(msg) { + return len(msg), &Error{err: "overflow packing wks"} + } + msg[off+serv/8] |= byte(1 << (7 - uint(serv%8))) + if off+serv/8+1 > off1 { + off1 = off + serv/8 + 1 } - bit := uint16(serv) - bitmapbyte*8 - msg[bitmapbyte] = byte(1 << (7 - bit)) } - off += int(bitmapbyte) + off = off1 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"} - } + var lastwindow, lastlength uint16 for j := 0; j < val.Field(i).Len(); j++ { - t := uint16((fv.Index(j).Uint())) - window := uint16(t / 256) - if lastwindow != window { + t := uint16(fv.Index(j).Uint()) + window := t / 256 + length := (t-window*256)/8 + 1 + if window > lastwindow && lastlength != 0 { // New window, jump to the new offset - off += int(length) + 3 - if off > lenmsg { - return lenmsg, &Error{err: "overflow packing nsecx bitmap"} - } + off += int(lastlength) + 2 + lastlength = 0 } - 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"} + if window < lastwindow || length < lastlength { + return len(msg), &Error{err: "nsec bits out of order"} + } + if off+2+int(length) > len(msg) { + return len(msg), &Error{err: "overflow packing nsec"} } - // Setting the window # msg[off] = byte(window) // Setting the octets length - msg[off+1] = byte(length + 1) + msg[off+1] = byte(length) // 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"} + msg[off+1+int(length)] |= byte(1 << (7 - (t % 8))) + lastwindow, lastlength = window, length } + off += int(lastlength) + 2 } case reflect.Struct: off, err = packStructValue(fv, msg, off, compression, compress) @@ -821,6 +786,13 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str copy(msg[off:off+len(b64)], b64) off += len(b64) case `dns:"domain-name"`: + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, 1 and 2 or used for addresses + x := val.Field(2).Uint() + if x == 1 || x == 2 { + continue + } + } if off, err = PackDomainName(s, msg, off, compression, false && compress); err != nil { return lenmsg, err } @@ -859,6 +831,12 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str // length of string. String is RAW (not encoded in hex, nor base64) copy(msg[off:off+len(s)], s) off += len(s) + case `dns:"octet"`: + bytesTmp := make([]byte, 256) + off, err = packOctetString(fv.String(), msg, off, bytesTmp) + if err != nil { + return lenmsg, err + } case `dns:"txt"`: fallthrough case "": @@ -890,17 +868,11 @@ func packStructCompress(any interface{}, msg []byte, off int, compression map[st return off, err } -// TODO(miek): 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 lenrd int lenmsg := len(msg) for i := 0; i < val.NumField(); i++ { - if lenrd != 0 && lenrd == off { - break - } if off > lenmsg { return lenmsg, &Error{"bad offset unpacking"} } @@ -912,7 +884,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er // therefore it's expected that this interface would be PrivateRdata switch data := fv.Interface().(type) { case PrivateRdata: - n, err := data.Unpack(msg[off:lenrd]) + n, err := data.Unpack(msg[off:]) if err != nil { return lenmsg, err } @@ -926,9 +898,9 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er 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 servers []string var s string - for off < lenrd { + for off < lenmsg { s, off, err = UnpackDomainName(msg, off) if err != nil { return lenmsg, err @@ -937,30 +909,30 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er } fv.Set(reflect.ValueOf(servers)) case `dns:"txt"`: - if off == lenmsg || lenrd == off { + if off == lenmsg { break } var txt []string - txt, off, err = unpackTxt(msg, off, lenrd) + txt, off, err = unpackTxt(msg, off) if err != nil { return lenmsg, err } fv.Set(reflect.ValueOf(txt)) case `dns:"opt"`: // edns0 - if off == lenrd { + if off == lenmsg { // This is an EDNS0 (OPT Record) with no rdata // We can safely return here. break } - edns := make([]EDNS0, 0) + var edns []EDNS0 Option: code := uint16(0) - if off+2 > lenmsg { + if off+4 > lenmsg { return lenmsg, &Error{err: "overflow unpacking opt"} } code, off = unpackUint16(msg, off) optlen, off1 := unpackUint16(msg, off) - if off1+int(optlen) > lenrd { + if off1+int(optlen) > lenmsg { return lenmsg, &Error{err: "overflow unpacking opt"} } switch code { @@ -1017,27 +989,44 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er edns = append(edns, e) off = off1 + int(optlen) default: - // do nothing? + e := new(EDNS0_LOCAL) + e.Code = code + if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil { + return lenmsg, err + } + edns = append(edns, e) off = off1 + int(optlen) } - if off < lenrd { + if off < lenmsg { goto Option } fv.Set(reflect.ValueOf(edns)) case `dns:"a"`: - if off == lenrd { + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, must be 1 + if val.Field(2).Uint() != 1 { + continue + } + } + if off == lenmsg { break // dyn. update } - if off+net.IPv4len > lenrd || off+net.IPv4len > lenmsg { + 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 == lenrd { + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, must be 2 + if val.Field(2).Uint() != 2 { + continue + } + } + if off == lenmsg { break } - if off+net.IPv6len > lenrd || off+net.IPv6len > lenmsg { + 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], @@ -1046,9 +1035,9 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er off += net.IPv6len case `dns:"wks"`: // Rest of the record is the bitmap - serv := make([]uint16, 0) + var serv []uint16 j := 0 - for off < lenrd { + for off < lenmsg { if off+1 > lenmsg { return lenmsg, &Error{err: "overflow unpacking wks"} } @@ -1083,36 +1072,39 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er } fv.Set(reflect.ValueOf(serv)) case `dns:"nsec"`: // NSEC/NSEC3 - if off == lenrd { + if off == len(msg) { break } // Rest of the record is the type bitmap - if off+2 > lenrd || off+2 > lenmsg { - return lenmsg, &Error{err: "overflow unpacking nsecx"} - } - nsec := make([]uint16, 0) + var nsec []uint16 length := 0 window := 0 - for off+2 < lenrd { + lastwindow := -1 + for off < len(msg) { + if off+2 > len(msg) { + return len(msg), &Error{err: "overflow unpacking nsecx"} + } window = int(msg[off]) length = int(msg[off+1]) - //println("off, windows, length, end", off, window, length, endrr) + off += 2 + if window <= lastwindow { + // RFC 4034: Blocks are present in the NSEC RR RDATA in + // increasing numerical order. + return len(msg), &Error{err: "out of order NSEC block"} + } if length == 0 { - // 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, &Error{err: "overflow unpacking nsecx"} + // RFC 4034: Blocks with no types present MUST NOT be included. + return len(msg), &Error{err: "empty NSEC block"} } if length > 32 { - return lenmsg, &Error{err: "overflow unpacking nsecx"} + return len(msg), &Error{err: "NSEC block too long"} + } + if off+length > len(msg) { + return len(msg), &Error{err: "overflowing NSEC block"} } - // Walk the bytes in the window - and check the bit settings... - off += 2 + // Walk the bytes in the window and extract the type bits for j := 0; j < length; j++ { - if off+j+1 > lenmsg { - return lenmsg, &Error{err: "overflow unpacking nsecx"} - } b := msg[off+j] // Check the bits one by one, and set the type if b&0x80 == 0x80 { @@ -1141,6 +1133,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er } } off += length + lastwindow = window } fv.Set(reflect.ValueOf(nsec)) } @@ -1150,7 +1143,12 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er return lenmsg, err } if val.Type().Field(i).Name == "Hdr" { - lenrd = off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) + lenrd := off + int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) + if lenrd > lenmsg { + return lenmsg, &Error{err: "overflowing header size"} + } + msg = msg[:lenrd] + lenmsg = len(msg) } case reflect.Uint8: if off == lenmsg { @@ -1181,6 +1179,9 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er fv.SetUint(uint64(uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]))) off += 4 case reflect.Uint64: + if off == lenmsg { + break + } switch val.Type().Field(i).Tag { default: if off+8 > lenmsg { @@ -1206,32 +1207,42 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er switch val.Type().Field(i).Tag { default: return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")} + case `dns:"octet"`: + s = string(msg[off:]) + off = lenmsg case `dns:"hex"`: - hexend := lenrd + hexend := lenmsg if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) { hexend = off + int(val.FieldByName("HitLength").Uint()) } - if hexend > lenrd || hexend > lenmsg { - return lenmsg, &Error{err: "overflow unpacking hex"} + if hexend > lenmsg { + return lenmsg, &Error{err: "overflow unpacking HIP hex"} } s = hex.EncodeToString(msg[off:hexend]) off = hexend case `dns:"base64"`: // Rest of the RR is base64 encoded value - b64end := lenrd + b64end := lenmsg if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) { b64end = off + int(val.FieldByName("PublicKeyLength").Uint()) } - if b64end > lenrd || b64end > lenmsg { - return lenmsg, &Error{err: "overflow unpacking base64"} + if b64end > lenmsg { + return lenmsg, &Error{err: "overflow unpacking HIP base64"} } s = toBase64(msg[off:b64end]) off = b64end case `dns:"cdomain-name"`: fallthrough case `dns:"domain-name"`: - if off == lenmsg { - // zero rdata foo, OK for dyn. updates + if val.Type().String() == "dns.IPSECKEY" { + // Field(2) is GatewayType, 1 and 2 or used for addresses + x := val.Field(2).Uint() + if x == 1 || x == 2 { + continue + } + } + if off == lenmsg && int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) == 0 { + // zero rdata is ok for dyn updates, but only if rdlength is 0 break } s, off, err = UnpackDomainName(msg, off) @@ -1375,7 +1386,7 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { } end := off + int(h.Rdlength) // make an rr of that type and re-unpack. - mk, known := typeToRR[h.Rrtype] + mk, known := TypeToRR[h.Rrtype] if !known { rr = new(RFC3597) } else { @@ -1388,6 +1399,32 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { return rr, off, err } +// unpackRRslice unpacks msg[off:] into an []RR. +// If we cannot unpack the whole array, then it will return nil +func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) { + var r RR + // Optimistically make dst be the length that was sent + dst := make([]RR, 0, l) + for i := 0; i < l; i++ { + off1 := off + r, off, err = UnpackRR(msg, off) + if err != nil { + off = len(msg) + break + } + // If offset does not increase anymore, l is a lie + if off1 == off { + l = i + break + } + dst = append(dst, r) + } + if err != nil && off == len(msg) { + dst = nil + } + return dst, off, err +} + // Reverse a map func reverseInt8(m map[uint8]string) map[string]uint8 { n := make(map[string]uint8) @@ -1586,52 +1623,48 @@ func (dns *Msg) Unpack(msg []byte) (err error) { 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) + // Optimistically use the count given to us in the header + dns.Question = make([]Question, 0, int(dh.Qdcount)) - for i := 0; i < len(dns.Question); i++ { - off, err = UnpackStruct(&dns.Question[i], msg, off) + var q Question + for i := 0; i < int(dh.Qdcount); i++ { + off1 := off + off, err = UnpackStruct(&q, msg, off) if err != nil { + // Even if Truncated is set, we only will set ErrTruncated if we + // actually got the questions return err } - } - // If we see a TC bit being set we return here, without - // an error, because technically it isn't an error. So return - // without parsing the potentially corrupt packet and hitting an error. - // TODO(miek): this isn't the best strategy! - if dns.Truncated { - dns.Answer = nil - dns.Ns = nil - dns.Extra = nil - return nil - } - for i := 0; i < len(dns.Answer); i++ { - dns.Answer[i], off, err = UnpackRR(msg, off) - if err != nil { - return err + if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie! + dh.Qdcount = uint16(i) + break } + dns.Question = append(dns.Question, q) } - for i := 0; i < len(dns.Ns); i++ { - dns.Ns[i], off, err = UnpackRR(msg, off) - if err != nil { - return err - } + + dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off) + // The header counts might have been wrong so we need to update it + dh.Ancount = uint16(len(dns.Answer)) + if err == nil { + dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off) } - for i := 0; i < len(dns.Extra); i++ { - dns.Extra[i], off, err = UnpackRR(msg, off) - if err != nil { - return err - } + // The header counts might have been wrong so we need to update it + dh.Nscount = uint16(len(dns.Ns)) + if err == nil { + dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off) } + // The header counts might have been wrong so we need to update it + dh.Arcount = uint16(len(dns.Extra)) if off != len(msg) { // TODO(miek) make this an error? // use PackOpt to let people tell how detailed the error reporting should be? // println("dns: extra bytes in dns packet", off, "<", len(msg)) + } else if dns.Truncated { + // Whether we ran into a an error or not, we want to return that it + // was truncated + err = ErrTruncated } - return nil + return err } // Convert a complete message to a string with dig-like output. @@ -1863,9 +1896,18 @@ func Copy(r RR) RR { return r1 } +// Len returns the length (in octets) of the uncompressed RR in wire format. +func Len(r RR) int { + return r.len() +} + // Copy returns a new *Msg which is a deep-copy of dns. func (dns *Msg) Copy() *Msg { - r1 := new(Msg) + return dns.CopyTo(new(Msg)) +} + +// CopyTo copies the contents to the provided message using a deep-copy and returns the copy. +func (dns *Msg) CopyTo(r1 *Msg) *Msg { r1.MsgHdr = dns.MsgHdr r1.Compress = dns.Compress @@ -1874,25 +1916,34 @@ func (dns *Msg) Copy() *Msg { copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy } + rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) + var rri int + if len(dns.Answer) > 0 { - r1.Answer = make([]RR, len(dns.Answer)) + rrbegin := rri for i := 0; i < len(dns.Answer); i++ { - r1.Answer[i] = dns.Answer[i].copy() + rrArr[rri] = dns.Answer[i].copy() + rri++ } + r1.Answer = rrArr[rrbegin:rri:rri] } if len(dns.Ns) > 0 { - r1.Ns = make([]RR, len(dns.Ns)) + rrbegin := rri for i := 0; i < len(dns.Ns); i++ { - r1.Ns[i] = dns.Ns[i].copy() + rrArr[rri] = dns.Ns[i].copy() + rri++ } + r1.Ns = rrArr[rrbegin:rri:rri] } if len(dns.Extra) > 0 { - r1.Extra = make([]RR, len(dns.Extra)) + rrbegin := rri for i := 0; i < len(dns.Extra); i++ { - r1.Extra[i] = dns.Extra[i].copy() + rrArr[rri] = dns.Extra[i].copy() + rri++ } + r1.Extra = rrArr[rrbegin:rri:rri] } return r1 diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go index ac48da0f5ac..d2392c6ec60 100644 --- a/vendor/github.com/miekg/dns/nsecx.go +++ b/vendor/github.com/miekg/dns/nsecx.go @@ -50,6 +50,8 @@ func HashName(label string, ha uint8, iter uint16, salt string) string { return toBase32(nsec3) } +// Denialer is an interface that should be implemented by types that are used to denial +// answers in DNSSEC. type Denialer interface { // Cover will check if the (unhashed) name is being covered by this NSEC or NSEC3. Cover(name string) bool diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index 225713743c9..c78f8859120 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -1,10 +1,3 @@ -/* -PRIVATE RR - -RFC 6895 sets aside a range of type codes for private use. This range -is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these -can be used, before requesting an official type code from IANA. -*/ package dns import ( @@ -40,7 +33,7 @@ type PrivateRR struct { func mkPrivateRR(rrtype uint16) *PrivateRR { // Panics if RR is not an instance of PrivateRR. - rrfunc, ok := typeToRR[rrtype] + rrfunc, ok := TypeToRR[rrtype] if !ok { panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype)) } @@ -50,11 +43,13 @@ func mkPrivateRR(rrtype uint16) *PrivateRR { case *PrivateRR: return rr } - panic(fmt.Sprintf("dns: RR is not a PrivateRR, typeToRR[%d] generator returned %T", rrtype, anyrr)) + panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr)) } +// Header return the RR header of r. func (r *PrivateRR) Header() *RR_Header { return &r.Hdr } -func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() } + +func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() } // Private len and copy parts to satisfy RR interface. func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() } @@ -76,7 +71,7 @@ func (r *PrivateRR) copy() RR { func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) { rtypestr = strings.ToUpper(rtypestr) - typeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} } + TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} } TypeToString[rtype] = rtypestr StringToType[rtypestr] = rtype @@ -91,9 +86,9 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) // TODO(miek): we could also be returning _QUOTE, this might or might not // be an issue (basically parsing TXT becomes hard) switch l = <-c; l.value { - case _NEWLINE, _EOF: + case zNewline, zEOF: break FETCH - case _STRING: + case zString: text = append(text, l.token) } } @@ -113,7 +108,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) func PrivateHandleRemove(rtype uint16) { rtypestr, ok := TypeToString[rtype] if ok { - delete(typeToRR, rtype) + delete(TypeToRR, rtype) delete(TypeToString, rtype) delete(typeToparserFunc, rtype) delete(StringToType, rtypestr) diff --git a/vendor/github.com/miekg/dns/rawmsg.go b/vendor/github.com/miekg/dns/rawmsg.go index f138b7761df..b4a706b9388 100644 --- a/vendor/github.com/miekg/dns/rawmsg.go +++ b/vendor/github.com/miekg/dns/rawmsg.go @@ -21,7 +21,7 @@ func rawSetQuestionLen(msg []byte, i uint16) bool { return true } -// rawSetAnswerLen sets the lenght of the answer section. +// rawSetAnswerLen sets the length of the answer section. func rawSetAnswerLen(msg []byte, i uint16) bool { if len(msg) < 8 { return false @@ -30,7 +30,7 @@ func rawSetAnswerLen(msg []byte, i uint16) bool { return true } -// rawSetsNsLen sets the lenght of the authority section. +// rawSetsNsLen sets the length of the authority section. func rawSetNsLen(msg []byte, i uint16) bool { if len(msg) < 10 { return false @@ -39,7 +39,7 @@ func rawSetNsLen(msg []byte, i uint16) bool { return true } -// rawSetExtraLen sets the lenght of the additional section. +// rawSetExtraLen sets the length of the additional section. func rawSetExtraLen(msg []byte, i uint16) bool { if len(msg) < 12 { return false diff --git a/vendor/github.com/miekg/dns/sanitize.go b/vendor/github.com/miekg/dns/sanitize.go new file mode 100644 index 00000000000..b489f3f050b --- /dev/null +++ b/vendor/github.com/miekg/dns/sanitize.go @@ -0,0 +1,84 @@ +package dns + +// Dedup removes identical RRs from rrs. It preserves the original ordering. +// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies +// rrs. +// m is used to store the RRs temporay. If it is nil a new map will be allocated. +func Dedup(rrs []RR, m map[string]RR) []RR { + if m == nil { + m = make(map[string]RR) + } + // Save the keys, so we don't have to call normalizedString twice. + keys := make([]*string, 0, len(rrs)) + + for _, r := range rrs { + key := normalizedString(r) + keys = append(keys, &key) + if _, ok := m[key]; ok { + // Shortest TTL wins. + if m[key].Header().Ttl > r.Header().Ttl { + m[key].Header().Ttl = r.Header().Ttl + } + continue + } + + m[key] = r + } + // If the length of the result map equals the amount of RRs we got, + // it means they were all different. We can then just return the original rrset. + if len(m) == len(rrs) { + return rrs + } + + j := 0 + for i, r := range rrs { + // If keys[i] lives in the map, we should copy and remove it. + if _, ok := m[*keys[i]]; ok { + delete(m, *keys[i]) + rrs[j] = r + j++ + } + + if len(m) == 0 { + break + } + } + + return rrs[:j] +} + +// normalizedString returns a normalized string from r. The TTL +// is removed and the domain name is lowercased. We go from this: +// DomainNameTTLCLASSTYPERDATA to: +// lowercasenameCLASSTYPE... +func normalizedString(r RR) string { + // A string Go DNS makes has: domainnameTTL... + b := []byte(r.String()) + + // find the first non-escaped tab, then another, so we capture where the TTL lives. + esc := false + ttlStart, ttlEnd := 0, 0 + for i := 0; i < len(b) && ttlEnd == 0; i++ { + switch { + case b[i] == '\\': + esc = !esc + case b[i] == '\t' && !esc: + if ttlStart == 0 { + ttlStart = i + continue + } + if ttlEnd == 0 { + ttlEnd = i + } + case b[i] >= 'A' && b[i] <= 'Z' && !esc: + b[i] += 32 + default: + esc = false + } + } + + // remove TTL. + copy(b[ttlStart:], b[ttlEnd:]) + cut := ttlEnd - ttlStart + return string(b[:len(b)-cut]) +} diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index c250ccc26a1..edc5c6258c9 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -4,12 +4,17 @@ package dns import ( "bytes" + "crypto/tls" "io" "net" "sync" "time" ) +// Maximum number of TCP queries before we close the socket. +const maxTCPQueries = 128 + +// Handler is implemented by any value that implements ServeDNS. type Handler interface { ServeDNS(w ResponseWriter, r *Msg) } @@ -43,9 +48,10 @@ type response struct { tsigRequestMAC string tsigSecret map[string]string // the tsig secrets udp *net.UDPConn // i/o connection if UDP was used - tcp *net.TCPConn // i/o connection if TCP was used - udpSession *sessionUDP // oob data to get egress interface right + tcp net.Conn // i/o connection if TCP was used + udpSession *SessionUDP // oob data to get egress interface right remoteAddr net.Addr // address of the client + writer Writer // writer to output the raw DNS bits } // ServeMux is an DNS request multiplexer. It matches the @@ -72,12 +78,12 @@ var DefaultServeMux = NewServeMux() // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Msg) -// ServerDNS calls f(w, r) +// ServeDNS calls f(w, r). func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) { f(w, r) } -// FailedHandler returns a HandlerFunc that returns SERVFAIL for every request it gets. +// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets. func HandleFailed(w ResponseWriter, r *Msg) { m := new(Msg) m.SetRcode(r, RcodeServerFailure) @@ -87,13 +93,35 @@ func HandleFailed(w ResponseWriter, r *Msg) { func failedHandler() Handler { return HandlerFunc(HandleFailed) } -// ListenAndServe Starts a server on addresss and network speficied. Invoke handler +// ListenAndServe Starts a server on address and network specified Invoke handler // for incoming queries. func ListenAndServe(addr string, network string, handler Handler) error { server := &Server{Addr: addr, Net: network, Handler: handler} return server.ListenAndServe() } +// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in +// http://golang.org/pkg/net/http/#ListenAndServeTLS +func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return err + } + + config := tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + server := &Server{ + Addr: addr, + Net: "tcp-tls", + TLSConfig: &config, + Handler: handler, + } + + return server.ListenAndServe() +} + // ActivateAndServe activates a server with a listener from systemd, // l and p should not both be non-nil. // If both l and p are not nil only p will be used. @@ -121,10 +149,9 @@ func (mux *ServeMux) match(q string, t uint16) Handler { 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 } + // 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 { @@ -148,7 +175,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.m.Unlock() } -// Handle adds a handler to the ServeMux for pattern. +// HandleFunc adds a handler function to the ServeMux for pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { mux.Handle(pattern, HandlerFunc(handler)) } @@ -158,9 +185,9 @@ func (mux *ServeMux) HandleRemove(pattern string) { if pattern == "" { panic("dns: invalid pattern " + pattern) } - // don't need a mutex here, because deleting is OK, even if the - // entry is note there. + mux.m.Lock() delete(mux.z, Fqdn(pattern)) + mux.m.Unlock() } // ServeDNS dispatches the request to the handler whose @@ -197,14 +224,53 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { DefaultServeMux.HandleFunc(pattern, handler) } +// Writer writes raw DNS messages; each call to Write should send an entire message. +type Writer interface { + io.Writer +} + +// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message. +type Reader interface { + // ReadTCP reads a raw message from a TCP connection. Implementations may alter + // connection properties, for example the read-deadline. + ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) + // ReadUDP reads a raw message from a UDP connection. Implementations may alter + // connection properties, for example the read-deadline. + ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) +} + +// defaultReader is an adapter for the Server struct that implements the Reader interface +// using the readTCP and readUDP func of the embedded Server. +type defaultReader struct { + *Server +} + +func (dr *defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { + return dr.readTCP(conn, timeout) +} + +func (dr *defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { + return dr.readUDP(conn, timeout) +} + +// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader. +// Implementations should never return a nil Reader. +type DecorateReader func(Reader) Reader + +// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer. +// Implementations should never return a nil Writer. +type DecorateWriter func(Writer) Writer + // A Server defines parameters for running an DNS server. type Server struct { // Address to listen on, ":dns" if empty. Addr string - // if "tcp" it will invoke a TCP listener, otherwise an UDP one. + // if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one Net string // TCP Listener to use, this is to aid in systemd's socket activation. Listener net.Listener + // TLS connection configuration + TLSConfig *tls.Config // UDP "Listener" to use, this is to aid in systemd's socket activation. PacketConn net.PacketConn // Handler to invoke, dns.DefaultServeMux if nil. @@ -221,31 +287,30 @@ type Server struct { // Secret(s) for Tsig map[]. TsigSecret map[string]string // Unsafe instructs the server to disregard any sanity checks and directly hand the message to - // the handler. It will specfically not check if the query has the QR bit not set. + // the handler. It will specifically not check if the query has the QR bit not set. Unsafe bool - // If NotifyStartedFunc is set is is called, once the server has started listening. + // If NotifyStartedFunc is set it is called once the server has started listening. NotifyStartedFunc func() + // DecorateReader is optional, allows customization of the process that reads raw DNS messages. + DecorateReader DecorateReader + // DecorateWriter is optional, allows customization of the process that writes raw DNS messages. + DecorateWriter DecorateWriter - // For graceful shutdown. - stopUDP chan bool - stopTCP chan bool - wgUDP sync.WaitGroup - wgTCP sync.WaitGroup + // Graceful shutdown handling - // make start/shutdown not racy - lock sync.Mutex + inFlight sync.WaitGroup + + lock sync.RWMutex started bool } // ListenAndServe starts a nameserver on the configured address in *Server. func (srv *Server) ListenAndServe() error { srv.lock.Lock() + defer srv.lock.Unlock() if srv.started { return &Error{err: "server already started"} } - srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool) - srv.started = true - srv.lock.Unlock() addr := srv.Addr if addr == "" { addr = ":domain" @@ -263,7 +328,30 @@ func (srv *Server) ListenAndServe() error { if e != nil { return e } - return srv.serveTCP(l) + srv.Listener = l + srv.started = true + srv.lock.Unlock() + e = srv.serveTCP(l) + srv.lock.Lock() // to satisfy the defer at the top + return e + case "tcp-tls", "tcp4-tls", "tcp6-tls": + network := "tcp" + if srv.Net == "tcp4-tls" { + network = "tcp4" + } else if srv.Net == "tcp6" { + network = "tcp6" + } + + l, e := tls.Listen(network, addr, srv.TLSConfig) + if e != nil { + return e + } + srv.Listener = l + srv.started = true + srv.lock.Unlock() + e = srv.serveTCP(l) + srv.lock.Lock() // to satisfy the defer at the top + return e case "udp", "udp4", "udp6": a, e := net.ResolveUDPAddr(srv.Net, addr) if e != nil { @@ -276,7 +364,12 @@ func (srv *Server) ListenAndServe() error { if e := setUDPSocketOptions(l); e != nil { return e } - return srv.serveUDP(l) + srv.PacketConn = l + srv.started = true + srv.lock.Unlock() + e = srv.serveUDP(l) + srv.lock.Lock() // to satisfy the defer at the top + return e } return &Error{err: "bad network"} } @@ -285,71 +378,62 @@ func (srv *Server) ListenAndServe() error { // configured in *Server. Its main use is to start a server from systemd. func (srv *Server) ActivateAndServe() error { srv.lock.Lock() + defer srv.lock.Unlock() if srv.started { return &Error{err: "server already started"} } - srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool) - srv.started = true - srv.lock.Unlock() - if srv.PacketConn != nil { + pConn := srv.PacketConn + l := srv.Listener + if pConn != nil { if srv.UDPSize == 0 { srv.UDPSize = MinMsgSize } - if t, ok := srv.PacketConn.(*net.UDPConn); ok { + if t, ok := pConn.(*net.UDPConn); ok { if e := setUDPSocketOptions(t); e != nil { return e } - return srv.serveUDP(t) + srv.started = true + srv.lock.Unlock() + e := srv.serveUDP(t) + srv.lock.Lock() // to satisfy the defer at the top + return e } } - if srv.Listener != nil { - if t, ok := srv.Listener.(*net.TCPListener); ok { - return srv.serveTCP(t) - } + if l != nil { + srv.started = true + srv.lock.Unlock() + e := srv.serveTCP(l) + srv.lock.Lock() // to satisfy the defer at the top + return e } return &Error{err: "bad listeners"} } // Shutdown gracefully shuts down a server. After a call to Shutdown, ListenAndServe and // ActivateAndServe will return. All in progress queries are completed before the server -// is taken down. If the Shutdown is taking longer than the reading timeout and error +// is taken down. If the Shutdown is taking longer than the reading timeout an error // is returned. func (srv *Server) Shutdown() error { srv.lock.Lock() if !srv.started { + srv.lock.Unlock() return &Error{err: "server not started"} } srv.started = false srv.lock.Unlock() - net, addr := srv.Net, srv.Addr - switch { - case srv.Listener != nil: - a := srv.Listener.Addr() - net, addr = a.Network(), a.String() - case srv.PacketConn != nil: - a := srv.PacketConn.LocalAddr() - net, addr = a.Network(), a.String() + + if srv.PacketConn != nil { + srv.PacketConn.Close() + } + if srv.Listener != nil { + srv.Listener.Close() } fin := make(chan bool) - switch net { - case "tcp", "tcp4", "tcp6": - go func() { - srv.stopTCP <- true - srv.wgTCP.Wait() - fin <- true - }() - - case "udp", "udp4", "udp6": - go func() { - srv.stopUDP <- true - srv.wgUDP.Wait() - fin <- true - }() - } - - c := &Client{Net: net} - go c.Exchange(new(Msg), addr) // extra query to help ReadXXX loop to pass + go func() { + srv.inFlight.Wait() + fin <- true + }() select { case <-time.After(srv.getReadTimeout()): @@ -369,14 +453,19 @@ func (srv *Server) getReadTimeout() time.Duration { } // serveTCP starts a TCP listener for the server. -// Each request is handled in a seperate goroutine. -func (srv *Server) serveTCP(l *net.TCPListener) error { +// Each request is handled in a separate goroutine. +func (srv *Server) serveTCP(l net.Listener) error { defer l.Close() if srv.NotifyStartedFunc != nil { srv.NotifyStartedFunc() } + reader := Reader(&defaultReader{srv}) + if srv.DecorateReader != nil { + reader = srv.DecorateReader(reader) + } + handler := srv.Handler if handler == nil { handler = DefaultServeMux @@ -384,27 +473,30 @@ func (srv *Server) serveTCP(l *net.TCPListener) error { rtimeout := srv.getReadTimeout() // deadline is not used here for { - rw, e := l.AcceptTCP() + rw, e := l.Accept() if e != nil { - continue + if neterr, ok := e.(net.Error); ok && neterr.Temporary() { + continue + } + return e } - m, e := srv.readTCP(rw, rtimeout) - select { - case <-srv.stopTCP: + m, e := reader.ReadTCP(rw, rtimeout) + srv.lock.RLock() + if !srv.started { + srv.lock.RUnlock() return nil - default: } + srv.lock.RUnlock() if e != nil { continue } - srv.wgTCP.Add(1) + srv.inFlight.Add(1) go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw) } - panic("dns: not reached") } // serveUDP starts a UDP listener for the server. -// Each request is handled in a seperate goroutine. +// Each request is handled in a separate goroutine. func (srv *Server) serveUDP(l *net.UDPConn) error { defer l.Close() @@ -412,6 +504,11 @@ func (srv *Server) serveUDP(l *net.UDPConn) error { srv.NotifyStartedFunc() } + reader := Reader(&defaultReader{srv}) + if srv.DecorateReader != nil { + reader = srv.DecorateReader(reader) + } + handler := srv.Handler if handler == nil { handler = DefaultServeMux @@ -419,35 +516,39 @@ func (srv *Server) serveUDP(l *net.UDPConn) error { rtimeout := srv.getReadTimeout() // deadline is not used here for { - m, s, e := srv.readUDP(l, rtimeout) - select { - case <-srv.stopUDP: + m, s, e := reader.ReadUDP(l, rtimeout) + srv.lock.RLock() + if !srv.started { + srv.lock.RUnlock() return nil - default: } + srv.lock.RUnlock() if e != nil { continue } - srv.wgUDP.Add(1) + srv.inFlight.Add(1) go srv.serve(s.RemoteAddr(), handler, m, l, s, nil) } - panic("dns: not reached") } // Serve a new connection. -func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *sessionUDP, t *net.TCPConn) { +func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t net.Conn) { + defer srv.inFlight.Done() + w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s} - q := 0 - defer func() { - if u != nil { - srv.wgUDP.Done() - } - if t != nil { - srv.wgTCP.Done() - } - }() + if srv.DecorateWriter != nil { + w.writer = srv.DecorateWriter(w) + } else { + w.writer = w + } + + q := 0 // counter for the amount of TCP queries we get + + reader := Reader(&defaultReader{srv}) + if srv.DecorateReader != nil { + reader = srv.DecorateReader(reader) + } Redo: - // Ideally we want use isMsg here before we allocate memory to actually parse the packet. req := new(Msg) err := req.Unpack(m) if err != nil { // Send a FormatError back @@ -475,6 +576,15 @@ Redo: h.ServeDNS(w, req) // Writes back to the client Exit: + if w.tcp == nil { + return + } + // TODO(miek): make this number configurable? + if q > maxTCPQueries { // close socket after this many queries + w.Close() + return + } + if w.hijacked { return // client calls Close() } @@ -486,21 +596,16 @@ Exit: if srv.IdleTimeout != nil { idleTimeout = srv.IdleTimeout() } - m, e := srv.readTCP(w.tcp, idleTimeout) + m, e := reader.ReadTCP(w.tcp, idleTimeout) if e == nil { q++ - // TODO(miek): make this number configurable? - if q > 128 { // close socket after this many queries - w.Close() - return - } goto Redo } w.Close() return } -func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, error) { +func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { conn.SetReadDeadline(time.Now().Add(timeout)) l := make([]byte, 2) n, err := conn.Read(l) @@ -535,10 +640,10 @@ func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, er return m, nil } -func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *sessionUDP, error) { +func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { conn.SetReadDeadline(time.Now().Add(timeout)) m := make([]byte, srv.UDPSize) - n, s, e := readFromSessionUDP(conn, m) + n, s, e := ReadFromSessionUDP(conn, m) if e != nil || n == 0 { if e != nil { return nil, nil, e @@ -558,7 +663,7 @@ func (w *response) WriteMsg(m *Msg) (err error) { if err != nil { return err } - _, err = w.Write(data) + _, err = w.writer.Write(data) return err } } @@ -566,7 +671,7 @@ func (w *response) WriteMsg(m *Msg) (err error) { if err != nil { return err } - _, err = w.Write(data) + _, err = w.writer.Write(data) return err } @@ -574,7 +679,7 @@ func (w *response) WriteMsg(m *Msg) (err error) { func (w *response) Write(m []byte) (int, error) { switch { case w.udp != nil: - n, err := writeToSessionUDP(w.udp, m, w.udpSession) + n, err := WriteToSessionUDP(w.udp, m, w.udpSession) return n, err case w.tcp != nil: lm := len(m) diff --git a/vendor/github.com/miekg/dns/sig0.go b/vendor/github.com/miekg/dns/sig0.go index d96b31be06b..0fccddbc150 100644 --- a/vendor/github.com/miekg/dns/sig0.go +++ b/vendor/github.com/miekg/dns/sig0.go @@ -1,25 +1,9 @@ -// SIG(0) -// -// From RFC 2931: -// -// SIG(0) provides protection for DNS transactions and requests .... -// ... protection for glue records, DNS requests, protection for message headers -// on requests and responses, and protection of the overall integrity of a response. -// -// It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared -// secret approach in TSIG. -// Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and -// RSASHA512. -// -// Signing subsequent messages in multi-message sessions is not implemented. -// package dns import ( "crypto" "crypto/dsa" "crypto/ecdsa" - "crypto/rand" "crypto/rsa" "math/big" "strings" @@ -29,7 +13,7 @@ import ( // Sign signs a dns.Msg. It fills the signature with the appropriate data. // The SIG record should have the SignerName, KeyTag, Algorithm, Inception // and Expiration set. -func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) { +func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { if k == nil { return nil, ErrPrivKey } @@ -57,56 +41,26 @@ func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) { return nil, err } buf = buf[:off:cap(buf)] - var hash crypto.Hash - var intlen int - switch rr.Algorithm { - case DSA, RSASHA1: - hash = crypto.SHA1 - case RSASHA256, ECDSAP256SHA256: - hash = crypto.SHA256 - intlen = 32 - case ECDSAP384SHA384: - hash = crypto.SHA384 - intlen = 48 - case RSASHA512: - hash = crypto.SHA512 - default: + + hash, ok := AlgorithmToHash[rr.Algorithm] + if !ok { return nil, ErrAlg } + hasher := hash.New() // Write SIG rdata hasher.Write(buf[len(mbuf)+1+2+2+4+2:]) // Write message hasher.Write(buf[:len(mbuf)]) - hashed := hasher.Sum(nil) - var sig []byte - switch p := k.(type) { - case *dsa.PrivateKey: - t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8) - r1, s1, err := dsa.Sign(rand.Reader, p, hashed) - if err != nil { - return nil, err - } - sig = append(sig, byte(t)) - sig = append(sig, intToBytes(r1, 20)...) - sig = append(sig, intToBytes(s1, 20)...) - case *rsa.PrivateKey: - sig, err = rsa.SignPKCS1v15(rand.Reader, p, hash, hashed) - if err != nil { - return nil, err - } - case *ecdsa.PrivateKey: - r1, s1, err := ecdsa.Sign(rand.Reader, p, hashed) - if err != nil { - return nil, err - } - sig = intToBytes(r1, intlen) - sig = append(sig, intToBytes(s1, intlen)...) - default: - return nil, ErrAlg + signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm) + if err != nil { + return nil, err } - rr.Signature = toBase64(sig) + + rr.Signature = toBase64(signature) + sig := string(signature) + buf = append(buf, sig...) if len(buf) > int(^uint16(0)) { return nil, ErrBuf @@ -118,7 +72,7 @@ func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) { buf[rdoff], buf[rdoff+1] = packUint16(rdlen) // Adjust additional count adc, _ := unpackUint16(buf, 10) - adc += 1 + adc++ buf[10], buf[11] = packUint16(adc) return buf, nil } @@ -246,7 +200,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { return rsa.VerifyPKCS1v15(pk, hash, hashed, sig) } case ECDSAP256SHA256, ECDSAP384SHA384: - pk := k.publicKeyCurve() + pk := k.publicKeyECDSA() r := big.NewInt(0) r.SetBytes(sig[:len(sig)/2]) s := big.NewInt(0) diff --git a/vendor/github.com/miekg/dns/tlsa.go b/vendor/github.com/miekg/dns/tlsa.go index d3bc3b0217f..0a550dc6cbf 100644 --- a/vendor/github.com/miekg/dns/tlsa.go +++ b/vendor/github.com/miekg/dns/tlsa.go @@ -25,7 +25,8 @@ func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (st h := sha256.New() switch selector { case 0: - return hex.EncodeToString(cert.Raw), nil + io.WriteString(h, string(cert.Raw)) + return hex.EncodeToString(h.Sum(nil)), nil case 1: io.WriteString(h, string(cert.RawSubjectPublicKeyInfo)) return hex.EncodeToString(h.Sum(nil)), nil @@ -34,7 +35,8 @@ func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (st h := sha512.New() switch selector { case 0: - return hex.EncodeToString(cert.Raw), nil + io.WriteString(h, string(cert.Raw)) + return hex.EncodeToString(h.Sum(nil)), nil case 1: io.WriteString(h, string(cert.RawSubjectPublicKeyInfo)) return hex.EncodeToString(h.Sum(nil)), nil @@ -80,5 +82,5 @@ func TLSAName(name, service, network string) (string, error) { if e != nil { return "", e } - return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil + return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil } diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go index 2c64ee8d548..c3374e19407 100644 --- a/vendor/github.com/miekg/dns/tsig.go +++ b/vendor/github.com/miekg/dns/tsig.go @@ -1,56 +1,3 @@ -// TRANSACTION SIGNATURE -// -// 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 zone transfer (almost all TSIG usage is when requesting zone transfers), with -// TSIG, this is the basic use pattern. In this example we request an AXFR for -// miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A==" -// and using the server 176.58.119.54: -// -// t := new(dns.Transfer) -// m := new(dns.Msg) -// t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} -// m.SetAxfr("miek.nl.") -// m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) -// c, err := t.In(m, "176.58.119.54:53") -// for r := range c { /* r.RR */ } -// -// You can now read the records from the transfer as they come in. Each envelope is checked with TSIG. -// If something is not correct an error is returned. -// -// Basic use pattern validating and replying to a message that has TSIG set. -// -// server := &dns.Server{Addr: ":53", Net: "udp"} -// server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} -// go server.ListenAndServe() -// dns.HandleFunc(".", handleRequest) -// -// func handleRequest(w dns.ResponseWriter, r *dns.Msg) { -// m := new(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 ( @@ -58,6 +5,7 @@ import ( "crypto/md5" "crypto/sha1" "crypto/sha256" + "crypto/sha512" "encoding/hex" "hash" "io" @@ -71,8 +19,11 @@ const ( HmacMD5 = "hmac-md5.sig-alg.reg.int." HmacSHA1 = "hmac-sha1." HmacSHA256 = "hmac-sha256." + HmacSHA512 = "hmac-sha512." ) +// TSIG is the RR the holds the transaction signature of a message. +// See RFC 2845 and RFC 4635. type TSIG struct { Hdr RR_Header Algorithm string `dns:"domain-name"` @@ -86,10 +37,6 @@ type TSIG struct { 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 { @@ -107,15 +54,6 @@ func (rr *TSIG) String() string { 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 { @@ -174,13 +112,15 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s t := new(TSIG) var h hash.Hash - switch rr.Algorithm { + switch strings.ToLower(rr.Algorithm) { case HmacMD5: h = hmac.New(md5.New, []byte(rawsecret)) case HmacSHA1: h = hmac.New(sha1.New, []byte(rawsecret)) case HmacSHA256: h = hmac.New(sha256.New, []byte(rawsecret)) + case HmacSHA512: + h = hmac.New(sha512.New, []byte(rawsecret)) default: return nil, "", ErrKeyAlg } @@ -238,13 +178,15 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { } var h hash.Hash - switch tsig.Algorithm { + switch strings.ToLower(tsig.Algorithm) { case HmacMD5: h = hmac.New(md5.New, rawsecret) case HmacSHA1: h = hmac.New(sha1.New, rawsecret) case HmacSHA256: h = hmac.New(sha256.New, rawsecret) + case HmacSHA512: + h = hmac.New(sha512.New, rawsecret) default: return ErrKeyAlg } diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index 16bb181b9b0..64143dba511 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -10,9 +10,12 @@ import ( ) type ( - Type uint16 // Type is a DNS type. - Class uint16 // Class is a DNS class. - Name string // Name is a DNS domain name. + // Type is a DNS type. + Type uint16 + // Class is a DNS class. + Class uint16 + // Name is a DNS domain name. + Name string ) // Packet formats @@ -20,6 +23,7 @@ type ( // Wire constants and supported types. const ( // valid RR_Header.Rrtype and Question.qtype + TypeNone uint16 = 0 TypeA uint16 = 1 TypeNS uint16 = 2 @@ -42,7 +46,6 @@ const ( TypeX25 uint16 = 19 TypeISDN uint16 = 20 TypeRT uint16 = 21 - TypeNSAP uint16 = 22 TypeNSAPPTR uint16 = 23 TypeSIG uint16 = 24 TypeKEY uint16 = 25 @@ -88,9 +91,12 @@ const ( TypeLP uint16 = 107 TypeEUI48 uint16 = 108 TypeEUI64 uint16 = 109 + TypeURI uint16 = 256 + TypeCAA uint16 = 257 TypeTKEY uint16 = 249 TypeTSIG uint16 = 250 + // valid Question.Qtype only TypeIXFR uint16 = 251 TypeAXFR uint16 = 252 @@ -98,8 +104,6 @@ const ( TypeMAILA uint16 = 254 TypeANY uint16 = 255 - TypeURI uint16 = 256 - TypeCAA uint16 = 257 TypeTA uint16 = 32768 TypeDLV uint16 = 32769 TypeReserved uint16 = 65535 @@ -112,7 +116,7 @@ const ( ClassNONE = 254 ClassANY = 255 - // Msg.rcode + // Message Response Codes. RcodeSuccess = 0 RcodeFormatError = 1 RcodeServerFailure = 2 @@ -133,16 +137,15 @@ const ( RcodeBadAlg = 21 RcodeBadTrunc = 22 // TSIG - // Opcode + // Message Opcodes. There is no 3. OpcodeQuery = 0 OpcodeIQuery = 1 OpcodeStatus = 2 - // There is no 3 OpcodeNotify = 4 OpcodeUpdate = 5 ) -// The wire format for the DNS packet header. +// Headers is the wire format for the DNS packet header. type Header struct { Id uint16 Bits uint16 @@ -150,6 +153,8 @@ type Header struct { } const ( + headerSize = 12 + // Header.Bits _QR = 1 << 15 // query/response (response=1) _AA = 1 << 10 // authoritative @@ -169,7 +174,7 @@ const ( LOC_ALTITUDEBASE = 100000 ) -// RFC 4398, Section 2.1 +// Different Certificate Types, see RFC 4398, Section 2.1 const ( CertPKIX = 1 + iota CertSPKI @@ -183,6 +188,8 @@ const ( CertOID = 254 ) +// CertTypeToString converts the Cert Type to its string representation. +// See RFC 4398 and RFC 6944. var CertTypeToString = map[uint16]string{ CertPKIX: "PKIX", CertSPKI: "SPKI", @@ -196,15 +203,23 @@ var CertTypeToString = map[uint16]string{ CertOID: "OID", } +// StringToCertType is the reverseof CertTypeToString. var StringToCertType = reverseInt16(CertTypeToString) -// DNS queries. +//go:generate go run types_generate.go + +// Question holds a DNS question. There can be multiple questions in the +// question section of a message. Usually there is just one. type Question struct { Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed) Qtype uint16 Qclass uint16 } +func (q *Question) len() int { + return len(q.Name) + 1 + 2 + 2 +} + func (q *Question) String() (s string) { // prefix with ; (as in dig) s = ";" + sprintName(q.Name) + "\t" @@ -213,30 +228,21 @@ func (q *Question) String() (s string) { return s } -func (q *Question) len() int { - l := len(q.Name) + 1 - return l + 4 -} - +// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY +// is named "*" there. type ANY struct { Hdr RR_Header // Does not have any rdata } -func (rr *ANY) 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() } +func (rr *ANY) String() string { return rr.Hdr.String() } 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(), sprintName(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 } +func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } type HINFO struct { Hdr RR_Header @@ -244,31 +250,23 @@ type HINFO struct { 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) } +func (rr *HINFO) String() string { + return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os}) +} type MB struct { Hdr RR_Header Mb string `dns:"cdomain-name"` } -func (rr *MB) Header() *RR_Header { return &rr.Hdr } -func (rr *MB) copy() RR { return &MB{*rr.Hdr.copyHeader(), sprintName(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 } +func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) } type MG struct { Hdr RR_Header Mg string `dns:"cdomain-name"` } -func (rr *MG) Header() *RR_Header { return &rr.Hdr } -func (rr *MG) copy() RR { return &MG{*rr.Hdr.copyHeader(), rr.Mg} } -func (rr *MG) len() int { l := len(rr.Mg) + 1; return rr.Hdr.len() + l } -func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) } +func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) } type MINFO struct { Hdr RR_Header @@ -276,28 +274,15 @@ type MINFO struct { 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() + sprintName(rr.Rmail) + " " + sprintName(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) len() int { l := len(rr.Mr) + 1; return rr.Hdr.len() + l } - func (rr *MR) String() string { return rr.Hdr.String() + sprintName(rr.Mr) } @@ -307,10 +292,6 @@ type MF struct { 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) len() int { return rr.Hdr.len() + len(rr.Mf) + 1 } - func (rr *MF) String() string { return rr.Hdr.String() + sprintName(rr.Mf) } @@ -320,10 +301,6 @@ type MD struct { 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) len() int { return rr.Hdr.len() + len(rr.Md) + 1 } - func (rr *MD) String() string { return rr.Hdr.String() + sprintName(rr.Md) } @@ -334,10 +311,6 @@ type MX struct { 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) len() int { l := len(rr.Mx) + 1; return rr.Hdr.len() + l + 2 } - func (rr *MX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx) } @@ -348,10 +321,6 @@ type AFSDB struct { 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) len() int { l := len(rr.Hostname) + 1; return rr.Hdr.len() + l + 2 } - func (rr *AFSDB) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname) } @@ -361,10 +330,6 @@ type X25 struct { 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) len() int { return rr.Hdr.len() + len(rr.PSDNAddress) + 1 } - func (rr *X25) String() string { return rr.Hdr.String() + rr.PSDNAddress } @@ -375,10 +340,6 @@ type RT struct { 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) len() int { l := len(rr.Host) + 1; return rr.Hdr.len() + l + 2 } - func (rr *RT) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host) } @@ -388,10 +349,6 @@ type NS struct { Ns string `dns:"cdomain-name"` } -func (rr *NS) Header() *RR_Header { return &rr.Hdr } -func (rr *NS) len() int { l := len(rr.Ns) + 1; return rr.Hdr.len() + l } -func (rr *NS) copy() RR { return &NS{*rr.Hdr.copyHeader(), rr.Ns} } - func (rr *NS) String() string { return rr.Hdr.String() + sprintName(rr.Ns) } @@ -401,10 +358,6 @@ type PTR struct { 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) len() int { l := len(rr.Ptr) + 1; return rr.Hdr.len() + l } - func (rr *PTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } @@ -415,10 +368,6 @@ type RP struct { 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) len() int { return rr.Hdr.len() + len(rr.Mbox) + 1 + len(rr.Txt) + 1 } - func (rr *RP) String() string { return rr.Hdr.String() + rr.Mbox + " " + sprintTxt([]string{rr.Txt}) } @@ -434,11 +383,6 @@ type SOA struct { 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() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) + " " + strconv.FormatInt(int64(rr.Serial), 10) + @@ -448,24 +392,11 @@ func (rr *SOA) String() string { " " + 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 { - cp := make([]string, len(rr.Txt), cap(rr.Txt)) - copy(cp, rr.Txt) - return &TXT{*rr.Hdr.copyHeader(), cp} -} - func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } func sprintName(s string) string { @@ -490,6 +421,34 @@ func sprintName(s string) string { return string(dst) } +func sprintTxtOctet(s string) string { + src := []byte(s) + dst := make([]byte, 0, len(src)) + dst = append(dst, '"') + for i := 0; i < len(src); { + if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' { + dst = append(dst, src[i:i+2]...) + i += 2 + } else { + b, n := nextByte(src, i) + if n == 0 { + i++ // dangling back slash + } else if b == '.' { + dst = append(dst, b) + } else { + if b < ' ' || b > '~' { + dst = appendByte(dst, b) + } else { + dst = append(dst, b) + } + } + i += n + } + } + dst = append(dst, '"') + return string(dst) +} + func sprintTxt(txt []string) string { var out []byte for i, s := range txt { @@ -532,21 +491,24 @@ func appendTXTStringByte(s []byte, b byte) []byte { return append(s, '\\', b) } if b < ' ' || b > '~' { - var buf [3]byte - bufs := strconv.AppendInt(buf[:0], int64(b), 10) - s = append(s, '\\') - for i := 0; i < 3-len(bufs); i++ { - s = append(s, '0') - } - for _, r := range bufs { - s = append(s, r) - } - return s - + return appendByte(s, b) } return append(s, b) } +func appendByte(s []byte, b byte) []byte { + var buf [3]byte + bufs := strconv.AppendInt(buf[:0], int64(b), 10) + s = append(s, '\\') + for i := 0; i < 3-len(bufs); i++ { + s = append(s, '0') + } + for _, r := range bufs { + s = append(s, r) + } + return s +} + func nextByte(b []byte, offset int) (byte, int) { if offset >= len(b) { return 0, 0 @@ -577,36 +539,13 @@ func nextByte(b []byte, offset int) (byte, int) { } } -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 { - cp := make([]string, len(rr.Txt), cap(rr.Txt)) - copy(cp, rr.Txt) - return &SPF{*rr.Hdr.copyHeader(), cp} -} - func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } -func (rr *SPF) len() int { - l := rr.Hdr.len() - for _, t := range rr.Txt { - l += len(t) + 1 - } - return l -} - type SRV struct { Hdr RR_Header Priority uint16 @@ -615,12 +554,6 @@ type SRV struct { Target string `dns:"domain-name"` } -func (rr *SRV) Header() *RR_Header { return &rr.Hdr } -func (rr *SRV) len() int { l := len(rr.Target) + 1; return rr.Hdr.len() + l + 6 } -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)) + " " + @@ -638,11 +571,6 @@ type NAPTR struct { 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)) + " " + @@ -653,12 +581,7 @@ func (rr *NAPTR) String() string { rr.Replacement } -func (rr *NAPTR) len() int { - return rr.Hdr.len() + 4 + len(rr.Flags) + 1 + len(rr.Service) + 1 + - len(rr.Regexp) + 1 + len(rr.Replacement) + 1 -} - -// See RFC 4398. +// The CERT resource record, see RFC 4398. type CERT struct { Hdr RR_Header Type uint16 @@ -667,11 +590,6 @@ type CERT struct { 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 { var ( ok bool @@ -689,21 +607,12 @@ func (rr *CERT) String() string { " " + rr.Certificate } -func (rr *CERT) len() int { - return rr.Hdr.len() + 5 + - base64.StdEncoding.DecodedLen(len(rr.Certificate)) -} - -// See RFC 2672. +// The DNAME resource record, 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) len() int { l := len(rr.Target) + 1; return rr.Hdr.len() + l } - func (rr *DNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } @@ -713,10 +622,6 @@ type A struct { A net.IP `dns:"a"` } -func (rr *A) Header() *RR_Header { return &rr.Hdr } -func (rr *A) copy() RR { return &A{*rr.Hdr.copyHeader(), copyIP(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() @@ -729,10 +634,6 @@ type AAAA struct { AAAA net.IP `dns:"aaaa"` } -func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } -func (rr *AAAA) copy() RR { return &AAAA{*rr.Hdr.copyHeader(), copyIP(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() @@ -747,12 +648,9 @@ type PX struct { Mapx400 string `dns:"domain-name"` } -func (rr *PX) Header() *RR_Header { return &rr.Hdr } -func (rr *PX) copy() RR { return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400} } func (rr *PX) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400) } -func (rr *PX) len() int { return rr.Hdr.len() + 2 + len(rr.Map822) + 1 + len(rr.Mapx400) + 1 } type GPOS struct { Hdr RR_Header @@ -761,11 +659,6 @@ type GPOS struct { Altitude string } -func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } -func (rr *GPOS) copy() RR { return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude} } -func (rr *GPOS) len() int { - return rr.Hdr.len() + len(rr.Longitude) + len(rr.Latitude) + len(rr.Altitude) + 3 -} func (rr *GPOS) String() string { return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude } @@ -781,12 +674,6 @@ type LOC struct { Altitude uint32 } -func (rr *LOC) Header() *RR_Header { return &rr.Hdr } -func (rr *LOC) len() int { return rr.Hdr.len() + 4 + 12 } -func (rr *LOC) copy() RR { - return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude} -} - // cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent // format and returns a string in m (two decimals for the cm) func cmToM(m, e uint8) string { @@ -801,12 +688,11 @@ func cmToM(m, e uint8) string { s := fmt.Sprintf("%d", m) for e > 2 { s += "0" - e -= 1 + e-- } return s } -// String returns a string version of a LOC func (rr *LOC) String() string { s := rr.Hdr.String() @@ -838,7 +724,7 @@ func (rr *LOC) String() string { lon = lon % LOC_HOURS s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, (float64(lon) / 1000), ew) - var alt float64 = float64(rr.Altitude) / 100 + var alt = float64(rr.Altitude) / 100 alt -= LOC_ALTITUDEBASE if rr.Altitude%100 != 0 { s += fmt.Sprintf("%.2fm ", alt) @@ -871,11 +757,6 @@ type RRSIG struct { 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() @@ -890,24 +771,12 @@ func (rr *RRSIG) String() string { 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 { - cp := make([]uint16, len(rr.TypeBitMap), cap(rr.TypeBitMap)) - copy(cp, rr.TypeBitMap) - return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, cp} -} - func (rr *NSEC) String() string { s := rr.Hdr.String() + sprintName(rr.NextDomain) for i := 0; i < len(rr.TypeBitMap); i++ { @@ -945,12 +814,6 @@ type DS struct { Digest string `dns:"hex"` } -func (rr *DS) Header() *RR_Header { return &rr.Hdr } -func (rr *DS) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 } -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)) + @@ -964,10 +827,6 @@ type KX struct { Exchanger string `dns:"domain-name"` } -func (rr *KX) Header() *RR_Header { return &rr.Hdr } -func (rr *KX) len() int { return rr.Hdr.len() + 2 + len(rr.Exchanger) + 1 } -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)) + " " + sprintName(rr.Exchanger) @@ -981,12 +840,6 @@ type TA struct { Digest string `dns:"hex"` } -func (rr *TA) Header() *RR_Header { return &rr.Hdr } -func (rr *TA) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 } -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)) + @@ -1000,10 +853,6 @@ type TALINK struct { 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) len() int { return rr.Hdr.len() + len(rr.PreviousName) + len(rr.NextName) + 2 } - func (rr *TALINK) String() string { return rr.Hdr.String() + sprintName(rr.PreviousName) + " " + sprintName(rr.NextName) @@ -1016,12 +865,6 @@ type SSHFP struct { FingerPrint string `dns:"hex"` } -func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr } -func (rr *SSHFP) len() int { return rr.Hdr.len() + 2 + len(rr.FingerPrint)/2 } -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)) + @@ -1029,30 +872,54 @@ func (rr *SSHFP) String() string { } type IPSECKEY struct { - Hdr RR_Header - Precedence uint8 + Hdr RR_Header + Precedence uint8 + // GatewayType: 1: A record, 2: AAAA record, 3: domainname. + // 0 is use for no type and GatewayName should be "." then. GatewayType uint8 Algorithm uint8 - Gateway string `dns:"ipseckey"` + // Gateway can be an A record, AAAA record or a domain name. + GatewayA net.IP `dns:"a"` + GatewayAAAA net.IP `dns:"aaaa"` + GatewayName string `dns:"domain-name"` PublicKey string `dns:"base64"` } -func (rr *IPSECKEY) 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)) + + s := rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) + " " + strconv.Itoa(int(rr.GatewayType)) + - " " + strconv.Itoa(int(rr.Algorithm)) + - " " + rr.Gateway + - " " + rr.PublicKey + " " + strconv.Itoa(int(rr.Algorithm)) + switch rr.GatewayType { + case 0: + fallthrough + case 3: + s += " " + rr.GatewayName + case 1: + s += " " + rr.GatewayA.String() + case 2: + s += " " + rr.GatewayAAAA.String() + default: + s += " ." + } + s += " " + rr.PublicKey + return s } func (rr *IPSECKEY) len() int { - return rr.Hdr.len() + 3 + len(rr.Gateway) + 1 + - base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + l := rr.Hdr.len() + 3 + 1 + switch rr.GatewayType { + default: + fallthrough + case 0: + fallthrough + case 3: + l += len(rr.GatewayName) + case 1: + l += 4 + case 2: + l += 16 + } + return l + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) } type KEY struct { @@ -1071,14 +938,6 @@ type DNSKEY struct { PublicKey string `dns:"base64"` } -func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *DNSKEY) len() int { - return rr.Hdr.len() + 4 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) -} -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)) + @@ -1094,12 +953,6 @@ type RKEY struct { PublicKey string `dns:"base64"` } -func (rr *RKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *RKEY) len() int { return rr.Hdr.len() + 4 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) } -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)) + @@ -1107,26 +960,12 @@ func (rr *RKEY) String() string { " " + rr.PublicKey } -type NSAP struct { - Hdr RR_Header - Length uint8 - Nsap string -} - -func (rr *NSAP) Header() *RR_Header { return &rr.Hdr } -func (rr *NSAP) copy() RR { return &NSAP{*rr.Hdr.copyHeader(), rr.Length, rr.Nsap} } -func (rr *NSAP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Length)) + " " + rr.Nsap } -func (rr *NSAP) len() int { return rr.Hdr.len() + 1 + len(rr.Nsap) + 1 } - type NSAPPTR struct { Hdr RR_Header Ptr string `dns:"domain-name"` } -func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr } -func (rr *NSAPPTR) copy() RR { return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr} } -func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } -func (rr *NSAPPTR) len() int { return rr.Hdr.len() + len(rr.Ptr) } +func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } type NSEC3 struct { Hdr RR_Header @@ -1140,13 +979,6 @@ type NSEC3 struct { TypeBitMap []uint16 `dns:"nsec"` } -func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr } -func (rr *NSEC3) copy() RR { - cp := make([]uint16, len(rr.TypeBitMap), cap(rr.TypeBitMap)) - copy(cp, rr.TypeBitMap) - return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, cp} -} - func (rr *NSEC3) String() string { s := rr.Hdr.String() s += strconv.Itoa(int(rr.Hash)) + @@ -1182,12 +1014,6 @@ type NSEC3PARAM struct { Salt string `dns:"hex"` } -func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr } -func (rr *NSEC3PARAM) len() int { return rr.Hdr.len() + 2 + 4 + 1 + len(rr.Salt)/2 } -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)) + @@ -1210,31 +1036,17 @@ type TKEY struct { 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 represents an unknown/generic 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) len() int { return rr.Hdr.len() + len(rr.Rdata)/2 + 2 } - func (rr *RFC3597) String() string { // Let's call it a hack s := rfc3597Header(rr.Hdr) @@ -1257,27 +1069,12 @@ 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 { - cp := make([]string, len(rr.Target), cap(rr.Target)) - copy(cp, rr.Target) - return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, cp} + Target string `dns:"octet"` } func (rr *URI) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + - " " + strconv.Itoa(int(rr.Weight)) + sprintTxt(rr.Target) -} - -func (rr *URI) len() int { - l := rr.Hdr.len() + 4 - for _, t := range rr.Target { - l += len(t) + 1 - } - return l + " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target) } type DHCID struct { @@ -1285,10 +1082,7 @@ type DHCID struct { 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)) } +func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest } type TLSA struct { Hdr RR_Header @@ -1298,13 +1092,6 @@ type TLSA struct { Certificate string `dns:"hex"` } -func (rr *TLSA) Header() *RR_Header { return &rr.Hdr } -func (rr *TLSA) len() int { return rr.Hdr.len() + 3 + len(rr.Certificate)/2 } - -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)) + @@ -1323,13 +1110,6 @@ type HIP struct { RendezvousServers []string `dns:"domain-name"` } -func (rr *HIP) Header() *RR_Header { return &rr.Hdr } -func (rr *HIP) copy() RR { - cp := make([]string, len(rr.RendezvousServers), cap(rr.RendezvousServers)) - copy(cp, rr.RendezvousServers) - return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, cp} -} - func (rr *HIP) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.PublicKeyAlgorithm)) + @@ -1341,38 +1121,13 @@ func (rr *HIP) String() string { 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 { - cp := make([]string, len(rr.ZSData), cap(rr.ZSData)) - copy(cp, rr.ZSData) - return &NINFO{*rr.Hdr.copyHeader(), cp} -} - func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) } -func (rr *NINFO) len() int { - l := rr.Hdr.len() - for _, t := range rr.ZSData { - l += len(t) + 1 - } - return l -} - type WKS struct { Hdr RR_Header Address net.IP `dns:"a"` @@ -1380,13 +1135,9 @@ type WKS struct { BitMap []uint16 `dns:"wks"` } -func (rr *WKS) Header() *RR_Header { return &rr.Hdr } -func (rr *WKS) len() int { return rr.Hdr.len() + net.IPv4len + 1 } - -func (rr *WKS) copy() RR { - cp := make([]uint16, len(rr.BitMap), cap(rr.BitMap)) - copy(cp, rr.BitMap) - return &WKS{*rr.Hdr.copyHeader(), copyIP(rr.Address), rr.Protocol, cp} +func (rr *WKS) len() int { + // TODO: this is missing something... + return rr.Hdr.len() + net.IPv4len + 1 } func (rr *WKS) String() (s string) { @@ -1394,6 +1145,7 @@ func (rr *WKS) String() (s string) { if rr.Address != nil { s += rr.Address.String() } + // TODO(miek): missing protocol here, see /etc/protocols for i := 0; i < len(rr.BitMap); i++ { // should lookup the port s += " " + strconv.Itoa(int(rr.BitMap[i])) @@ -1407,10 +1159,6 @@ type NID struct { 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) len() int { return rr.Hdr.len() + 2 + 8 } - func (rr *NID) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) node := fmt.Sprintf("%0.16x", rr.NodeID) @@ -1424,10 +1172,6 @@ type L32 struct { 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, copyIP(rr.Locator32)} } -func (rr *L32) len() int { return rr.Hdr.len() + net.IPv4len } - func (rr *L32) String() string { if rr.Locator32 == nil { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) @@ -1442,10 +1186,6 @@ type L64 struct { 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) len() int { return rr.Hdr.len() + 2 + 8 } - func (rr *L64) String() string { s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) node := fmt.Sprintf("%0.16X", rr.Locator64) @@ -1459,10 +1199,6 @@ type LP struct { 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) len() int { return rr.Hdr.len() + 2 + len(rr.Fqdn) + 1 } - func (rr *LP) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn) } @@ -1472,23 +1208,15 @@ type EUI48 struct { 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 } +func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) } 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 } +func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) } -// Support in incomplete - just handle it as unknown record -/* type CAA struct { Hdr RR_Header Flag uint8 @@ -1496,78 +1224,51 @@ type CAA struct { Value string `dns:"octet"` } -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) len() int { return rr.Hdr.len() + 1 + len(rr.Tag) + 1 + len(rr.Value) } - func (rr *CAA) String() string { - s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Flag), 10) + " " + rr.Tag - s += strconv.QuoteToASCII(rr.Value) - return s + return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value) } -*/ type UID struct { Hdr RR_Header Uid uint32 } -func (rr *UID) 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 } +func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) } type GID struct { Hdr RR_Header Gid uint32 } -func (rr *GID) 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 } +func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) } type UINFO struct { Hdr RR_Header Uinfo string } -func (rr *UINFO) 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() + sprintTxt([]string{rr.Uinfo}) } -func (rr *UINFO) len() int { return rr.Hdr.len() + len(rr.Uinfo) + 1 } +func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) } type EID struct { Hdr RR_Header Endpoint string `dns:"hex"` } -func (rr *EID) Header() *RR_Header { return &rr.Hdr } -func (rr *EID) copy() RR { return &EID{*rr.Hdr.copyHeader(), rr.Endpoint} } -func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) } -func (rr *EID) len() int { return rr.Hdr.len() + len(rr.Endpoint)/2 } +func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) } type NIMLOC struct { Hdr RR_Header Locator string `dns:"hex"` } -func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr } -func (rr *NIMLOC) copy() RR { return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator} } -func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) } -func (rr *NIMLOC) len() int { return rr.Hdr.len() + len(rr.Locator)/2 } +func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) } type OPENPGPKEY struct { Hdr RR_Header PublicKey string `dns:"base64"` } -func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *OPENPGPKEY) copy() RR { return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey} } -func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey } -func (rr *OPENPGPKEY) len() int { - return rr.Hdr.len() + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) -} +func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey } // TimeToString translates the RRSIG's incep. and expir. times to the // string representation used when printing the record. @@ -1625,73 +1326,3 @@ func copyIP(ip net.IP) net.IP { copy(p, ip) return p } - -// Map of constructors for each RR type. -var typeToRR = map[uint16]func() RR{ - TypeA: func() RR { return new(A) }, - TypeAAAA: func() RR { return new(AAAA) }, - TypeAFSDB: func() RR { return new(AFSDB) }, - // TypeCAA: func() RR { return new(CAA) }, - TypeCDS: func() RR { return new(CDS) }, - TypeCERT: func() RR { return new(CERT) }, - TypeCNAME: func() RR { return new(CNAME) }, - TypeDHCID: func() RR { return new(DHCID) }, - TypeDLV: func() RR { return new(DLV) }, - TypeDNAME: func() RR { return new(DNAME) }, - TypeKEY: func() RR { return new(KEY) }, - TypeDNSKEY: func() RR { return new(DNSKEY) }, - TypeDS: func() RR { return new(DS) }, - TypeEUI48: func() RR { return new(EUI48) }, - TypeEUI64: func() RR { return new(EUI64) }, - TypeGID: func() RR { return new(GID) }, - TypeGPOS: func() RR { return new(GPOS) }, - TypeEID: func() RR { return new(EID) }, - TypeHINFO: func() RR { return new(HINFO) }, - TypeHIP: func() RR { return new(HIP) }, - TypeKX: func() RR { return new(KX) }, - TypeL32: func() RR { return new(L32) }, - TypeL64: func() RR { return new(L64) }, - TypeLOC: func() RR { return new(LOC) }, - TypeLP: func() RR { return new(LP) }, - TypeMB: func() RR { return new(MB) }, - TypeMD: func() RR { return new(MD) }, - TypeMF: func() RR { return new(MF) }, - TypeMG: func() RR { return new(MG) }, - TypeMINFO: func() RR { return new(MINFO) }, - TypeMR: func() RR { return new(MR) }, - TypeMX: func() RR { return new(MX) }, - TypeNAPTR: func() RR { return new(NAPTR) }, - TypeNID: func() RR { return new(NID) }, - TypeNINFO: func() RR { return new(NINFO) }, - TypeNIMLOC: func() RR { return new(NIMLOC) }, - TypeNS: func() RR { return new(NS) }, - TypeNSAP: func() RR { return new(NSAP) }, - TypeNSAPPTR: func() RR { return new(NSAPPTR) }, - TypeNSEC3: func() RR { return new(NSEC3) }, - TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, - TypeNSEC: func() RR { return new(NSEC) }, - TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) }, - TypeOPT: func() RR { return new(OPT) }, - TypePTR: func() RR { return new(PTR) }, - TypeRKEY: func() RR { return new(RKEY) }, - TypeRP: func() RR { return new(RP) }, - TypePX: func() RR { return new(PX) }, - TypeSIG: func() RR { return new(SIG) }, - TypeRRSIG: func() RR { return new(RRSIG) }, - TypeRT: func() RR { return new(RT) }, - TypeSOA: func() RR { return new(SOA) }, - TypeSPF: func() RR { return new(SPF) }, - TypeSRV: func() RR { return new(SRV) }, - TypeSSHFP: func() RR { return new(SSHFP) }, - TypeTA: func() RR { return new(TA) }, - TypeTALINK: func() RR { return new(TALINK) }, - TypeTKEY: func() RR { return new(TKEY) }, - TypeTLSA: func() RR { return new(TLSA) }, - TypeTSIG: func() RR { return new(TSIG) }, - TypeTXT: func() RR { return new(TXT) }, - TypeUID: func() RR { return new(UID) }, - TypeUINFO: func() RR { return new(UINFO) }, - TypeURI: func() RR { return new(URI) }, - TypeWKS: func() RR { return new(WKS) }, - TypeX25: func() RR { return new(X25) }, -} diff --git a/vendor/github.com/miekg/dns/types_generate.go b/vendor/github.com/miekg/dns/types_generate.go new file mode 100644 index 00000000000..b8d1cd26b0c --- /dev/null +++ b/vendor/github.com/miekg/dns/types_generate.go @@ -0,0 +1,266 @@ +//+build ignore + +// types_generate.go is meant to run with go generate. It will use +// go/{importer,types} to track down all the RR struct types. Then for each type +// it will generate conversion tables (TypeToRR and TypeToString) and banal +// methods (len, Header, copy) based on the struct tags. The generated source is +// written to ztypes.go, and is meant to be checked into git. +package main + +import ( + "bytes" + "fmt" + "go/format" + "go/importer" + "go/types" + "log" + "os" + "strings" + "text/template" +) + +var skipLen = map[string]struct{}{ + "NSEC": {}, + "NSEC3": {}, + "OPT": {}, + "WKS": {}, + "IPSECKEY": {}, +} + +var packageHdr = ` +// *** DO NOT MODIFY *** +// AUTOGENERATED BY go generate + +package dns + +import ( + "encoding/base64" + "net" +) + +` + +var TypeToRR = template.Must(template.New("TypeToRR").Parse(` +// TypeToRR is a map of constructors for each RR type. +var TypeToRR = map[uint16]func() RR{ +{{range .}}{{if ne . "RFC3597"}} Type{{.}}: func() RR { return new({{.}}) }, +{{end}}{{end}} } + +`)) + +var typeToString = template.Must(template.New("typeToString").Parse(` +// TypeToString is a map of strings for each RR type. +var TypeToString = map[uint16]string{ +{{range .}}{{if ne . "NSAPPTR"}} Type{{.}}: "{{.}}", +{{end}}{{end}} TypeNSAPPTR: "NSAP-PTR", +} + +`)) + +var headerFunc = template.Must(template.New("headerFunc").Parse(` +// Header() functions +{{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr } +{{end}} + +`)) + +// getTypeStruct will take a type and the package scope, and return the +// (innermost) struct if the type is considered a RR type (currently defined as +// those structs beginning with a RR_Header, could be redefined as implementing +// the RR interface). The bool return value indicates if embedded structs were +// resolved. +func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { + st, ok := t.Underlying().(*types.Struct) + if !ok { + return nil, false + } + if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { + return st, false + } + if st.Field(0).Anonymous() { + st, _ := getTypeStruct(st.Field(0).Type(), scope) + return st, true + } + return nil, false +} + +func main() { + // Import and type-check the package + pkg, err := importer.Default().Import("github.com/miekg/dns") + fatalIfErr(err) + scope := pkg.Scope() + + // Collect constants like TypeX + var numberedTypes []string + for _, name := range scope.Names() { + o := scope.Lookup(name) + if o == nil || !o.Exported() { + continue + } + b, ok := o.Type().(*types.Basic) + if !ok || b.Kind() != types.Uint16 { + continue + } + if !strings.HasPrefix(o.Name(), "Type") { + continue + } + name := strings.TrimPrefix(o.Name(), "Type") + if name == "PrivateRR" { + continue + } + numberedTypes = append(numberedTypes, name) + } + + // Collect actual types (*X) + var namedTypes []string + for _, name := range scope.Names() { + o := scope.Lookup(name) + if o == nil || !o.Exported() { + continue + } + if st, _ := getTypeStruct(o.Type(), scope); st == nil { + continue + } + if name == "PrivateRR" { + continue + } + + // Check if corresponding TypeX exists + if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" { + log.Fatalf("Constant Type%s does not exist.", o.Name()) + } + + namedTypes = append(namedTypes, o.Name()) + } + + b := &bytes.Buffer{} + b.WriteString(packageHdr) + + // Generate TypeToRR + fatalIfErr(TypeToRR.Execute(b, namedTypes)) + + // Generate typeToString + fatalIfErr(typeToString.Execute(b, numberedTypes)) + + // Generate headerFunc + fatalIfErr(headerFunc.Execute(b, namedTypes)) + + // Generate len() + fmt.Fprint(b, "// len() functions\n") + for _, name := range namedTypes { + if _, ok := skipLen[name]; ok { + continue + } + o := scope.Lookup(name) + st, isEmbedded := getTypeStruct(o.Type(), scope) + if isEmbedded { + continue + } + fmt.Fprintf(b, "func (rr *%s) len() int {\n", name) + fmt.Fprintf(b, "l := rr.Hdr.len()\n") + for i := 1; i < st.NumFields(); i++ { + o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) } + + if _, ok := st.Field(i).Type().(*types.Slice); ok { + switch st.Tag(i) { + case `dns:"-"`: + // ignored + case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`: + o("for _, x := range rr.%s { l += len(x) + 1 }\n") + default: + log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) + } + continue + } + + switch st.Tag(i) { + case `dns:"-"`: + // ignored + case `dns:"cdomain-name"`, `dns:"domain-name"`: + o("l += len(rr.%s) + 1\n") + case `dns:"octet"`: + o("l += len(rr.%s)\n") + case `dns:"base64"`: + o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n") + case `dns:"size-hex"`, `dns:"hex"`: + o("l += len(rr.%s)/2 + 1\n") + case `dns:"a"`: + o("l += net.IPv4len // %s\n") + case `dns:"aaaa"`: + o("l += net.IPv6len // %s\n") + case `dns:"txt"`: + o("for _, t := range rr.%s { l += len(t) + 1 }\n") + case `dns:"uint48"`: + o("l += 6 // %s\n") + case "": + switch st.Field(i).Type().(*types.Basic).Kind() { + case types.Uint8: + o("l += 1 // %s\n") + case types.Uint16: + o("l += 2 // %s\n") + case types.Uint32: + o("l += 4 // %s\n") + case types.Uint64: + o("l += 8 // %s\n") + case types.String: + o("l += len(rr.%s) + 1\n") + default: + log.Fatalln(name, st.Field(i).Name()) + } + default: + log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) + } + } + fmt.Fprintf(b, "return l }\n") + } + + // Generate copy() + fmt.Fprint(b, "// copy() functions\n") + for _, name := range namedTypes { + o := scope.Lookup(name) + st, isEmbedded := getTypeStruct(o.Type(), scope) + if isEmbedded { + continue + } + fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name) + fields := []string{"*rr.Hdr.copyHeader()"} + for i := 1; i < st.NumFields(); i++ { + f := st.Field(i).Name() + if sl, ok := st.Field(i).Type().(*types.Slice); ok { + t := sl.Underlying().String() + t = strings.TrimPrefix(t, "[]") + t = strings.TrimPrefix(t, "github.com/miekg/dns.") + fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n", + f, t, f, f, f) + fields = append(fields, f) + continue + } + if st.Field(i).Type().String() == "net.IP" { + fields = append(fields, "copyIP(rr."+f+")") + continue + } + fields = append(fields, "rr."+f) + } + fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ",")) + fmt.Fprintf(b, "}\n") + } + + // gofmt + res, err := format.Source(b.Bytes()) + if err != nil { + b.WriteTo(os.Stderr) + log.Fatal(err) + } + + // write result + f, err := os.Create("ztypes.go") + fatalIfErr(err) + defer f.Close() + f.Write(res) +} + +func fatalIfErr(err error) { + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go index 0342543bf73..c79c6c88371 100644 --- a/vendor/github.com/miekg/dns/udp.go +++ b/vendor/github.com/miekg/dns/udp.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!plan9 package dns @@ -7,12 +7,15 @@ import ( "syscall" ) -type sessionUDP struct { +// SessionUDP holds the remote address and the associated +// out-of-band data. +type SessionUDP struct { raddr *net.UDPAddr context []byte } -func (s *sessionUDP) RemoteAddr() net.Addr { return s.raddr } +// RemoteAddr returns the remote network address. +func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } // setUDPSocketOptions sets the UDP socket options. // This function is implemented on a per platform basis. See udp_*.go for more details @@ -37,19 +40,19 @@ func setUDPSocketOptions(conn *net.UDPConn) error { return nil } -// readFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a +// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a // net.UDPAddr. -func readFromSessionUDP(conn *net.UDPConn, b []byte) (int, *sessionUDP, error) { +func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { oob := make([]byte, 40) n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) if err != nil { return n, nil, err } - return n, &sessionUDP{raddr, oob[:oobn]}, err + return n, &SessionUDP{raddr, oob[:oobn]}, err } -// writeToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *sessionUDP instead of a net.Addr. -func writeToSessionUDP(conn *net.UDPConn, b []byte, session *sessionUDP) (int, error) { +// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr. +func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr) return n, err } diff --git a/vendor/github.com/miekg/dns/udp_linux.go b/vendor/github.com/miekg/dns/udp_linux.go index 7a107857e13..c62d21881b6 100644 --- a/vendor/github.com/miekg/dns/udp_linux.go +++ b/vendor/github.com/miekg/dns/udp_linux.go @@ -24,6 +24,12 @@ func setUDPSocketOptions4(conn *net.UDPConn) error { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil { return err } + // Calling File() above results in the connection becoming blocking, we must fix that. + // See https://github.com/miekg/dns/issues/279 + err = syscall.SetNonblock(int(file.Fd()), true) + if err != nil { + return err + } return nil } @@ -36,6 +42,10 @@ func setUDPSocketOptions6(conn *net.UDPConn) error { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil { return err } + err = syscall.SetNonblock(int(file.Fd()), true) + if err != nil { + return err + } return nil } diff --git a/vendor/github.com/miekg/dns/udp_other.go b/vendor/github.com/miekg/dns/udp_other.go index c38dd3e7f0e..d40732441b0 100644 --- a/vendor/github.com/miekg/dns/udp_other.go +++ b/vendor/github.com/miekg/dns/udp_other.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!plan9 package dns diff --git a/vendor/github.com/miekg/dns/udp_plan9.go b/vendor/github.com/miekg/dns/udp_plan9.go new file mode 100644 index 00000000000..b794deeba0f --- /dev/null +++ b/vendor/github.com/miekg/dns/udp_plan9.go @@ -0,0 +1,34 @@ +package dns + +import ( + "net" +) + +func setUDPSocketOptions(conn *net.UDPConn) error { return nil } + +// SessionUDP holds the remote address and the associated +// out-of-band data. +type SessionUDP struct { + raddr *net.UDPAddr + context []byte +} + +// RemoteAddr returns the remote network address. +func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } + +// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a +// net.UDPAddr. +func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { + oob := make([]byte, 40) + n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) + if err != nil { + return n, nil, err + } + return n, &SessionUDP{raddr, oob[:oobn]}, err +} + +// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr. +func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { + n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr) + return n, err +} diff --git a/vendor/github.com/miekg/dns/udp_windows.go b/vendor/github.com/miekg/dns/udp_windows.go index 4c48723b565..2ce4b330028 100644 --- a/vendor/github.com/miekg/dns/udp_windows.go +++ b/vendor/github.com/miekg/dns/udp_windows.go @@ -4,28 +4,28 @@ package dns import "net" -type sessionUDP struct { +type SessionUDP struct { raddr *net.UDPAddr } -// readFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a +// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a // net.UDPAddr. -func readFromSessionUDP(conn *net.UDPConn, b []byte) (int, *sessionUDP, error) { +func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { n, raddr, err := conn.ReadFrom(b) if err != nil { return n, nil, err } - session := &sessionUDP{raddr.(*net.UDPAddr)} + session := &SessionUDP{raddr.(*net.UDPAddr)} return n, session, err } -// writeToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *sessionUDP instead of a net.Addr. -func writeToSessionUDP(conn *net.UDPConn, b []byte, session *sessionUDP) (int, error) { +// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr. +func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { n, err := conn.WriteTo(b, session.raddr) return n, err } -func (s *sessionUDP) RemoteAddr() net.Addr { return s.raddr } +func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } // setUDPSocketOptions sets the UDP socket options. // This function is implemented on a per platform basis. See udp_*.go for more details diff --git a/vendor/github.com/miekg/dns/update.go b/vendor/github.com/miekg/dns/update.go index 275d4e6cc27..e90c5c968ec 100644 --- a/vendor/github.com/miekg/dns/update.go +++ b/vendor/github.com/miekg/dns/update.go @@ -1,55 +1,24 @@ -// DYNAMIC UPDATES -// -// Dynamic updates reuses the DNS message format, but renames three of -// the sections. Question is Zone, Answer is Prerequisite, Authority is -// Update, only the Additional is not renamed. See RFC 2136 for the gory details. -// -// You can set a rather complex set of rules for the existence of absence of -// certain resource records or names in a zone to specify if resource records -// should be added or removed. The table from RFC 2136 supplemented with the Go -// DNS function shows which functions exist to specify the prerequisites. -// -// 3.2.4 - Table Of Metavalues Used In Prerequisite Section -// -// CLASS TYPE RDATA Meaning Function -// -------------------------------------------------------------- -// ANY ANY empty Name is in use dns.NameUsed -// ANY rrset empty RRset exists (value indep) dns.RRsetUsed -// NONE ANY empty Name is not in use dns.NameNotUsed -// NONE rrset empty RRset does not exist dns.RRsetNotUsed -// zone rrset rr RRset exists (value dep) dns.Used -// -// The prerequisite section can also be left empty. -// If you have decided on the prerequisites you can tell what RRs should -// be added or deleted. The next table shows the options you have and -// what functions to call. -// -// 3.4.2.6 - Table Of Metavalues Used In Update Section -// -// CLASS TYPE RDATA Meaning Function -// --------------------------------------------------------------- -// ANY ANY empty Delete all RRsets from name dns.RemoveName -// ANY rrset empty Delete an RRset dns.RemoveRRset -// NONE rrset rr Delete an RR from RRset dns.Remove -// zone rrset rr Add to an RRset dns.Insert -// 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}} + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) } } // NameNotUsed sets the RRs in the prereq section to // "Name is in not use" RRs. RFC 2136 section 2.4.5. func (u *Msg) NameNotUsed(rr []RR) { - 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}} + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}}) } } @@ -59,34 +28,34 @@ 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 + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + r.Header().Class = u.Question[0].Qclass + u.Answer = append(u.Answer, r) } } // RRsetUsed sets the RRs in the prereq section to // "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1. func (u *Msg) RRsetUsed(rr []RR) { - 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 + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}}) } } // RRsetNotUsed sets the RRs in the prereq section to // "RRset does not exist" RRs. RFC 2136 section 2.4.3. func (u *Msg) RRsetNotUsed(rr []RR) { - 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 + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassNONE}}) } } @@ -95,44 +64,43 @@ 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 + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + r.Header().Class = u.Question[0].Qclass + u.Ns = append(u.Ns, r) } } // RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2. func (u *Msg) RemoveRRset(rr []RR) { - m := make(map[RR_Header]struct{}) - u.Ns = make([]RR, 0, len(rr)) + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } for _, r := range rr { - h := *r.Header().copyHeader() - h.Class = ClassANY - h.Ttl = 0 - h.Rdlength = 0 - if _, ok := m[h]; ok { - continue - } - m[h] = struct{}{} - u.Ns = append(u.Ns, &ANY{h}) + u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}}) } } // RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3 func (u *Msg) RemoveName(rr []RR) { - u.Ns = make([]RR, len(rr)) - for i, r := range rr { - u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}} + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) } } -// Remove creates a dynamic update packet deletes RR from the RRSset, see RFC 2136 section 2.5.4 +// Remove creates a dynamic update packet deletes RR from a 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 + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + r.Header().Class = ClassNONE + r.Header().Ttl = 0 + u.Ns = append(u.Ns, r) } } diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go index 57bfb1676f9..7d3a67b8e45 100644 --- a/vendor/github.com/miekg/dns/xfr.go +++ b/vendor/github.com/miekg/dns/xfr.go @@ -13,9 +13,9 @@ type Envelope struct { // A Transfer defines parameters that are used during a zone transfer. type Transfer struct { *Conn - DialTimeout time.Duration // net.DialTimeout (ns), defaults to 2 * 1e9 - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections (ns), defaults to 2 * 1e9 - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections (ns), defaults to 2 * 1e9 + DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be fully qualified tsigTimersOnly bool } @@ -23,14 +23,26 @@ type Transfer struct { // Think we need to away to stop the transfer // In performs an incoming transfer with the server in a. +// If you would like to set the source IP, or some other attribute +// of a Dialer for a Transfer, you can do so by specifying the attributes +// in the Transfer.Conn: +// +// d := net.Dialer{LocalAddr: transfer_source} +// con, err := d.Dial("tcp", master) +// dnscon := &dns.Conn{Conn:con} +// transfer = &dns.Transfer{Conn: dnscon} +// channel, err := transfer.In(message, master) +// func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { timeout := dnsTimeout if t.DialTimeout != 0 { timeout = t.DialTimeout } - t.Conn, err = DialTimeout("tcp", a, timeout) - if err != nil { - return nil, err + if t.Conn == nil { + t.Conn, err = DialTimeout("tcp", a, timeout) + if err != nil { + return nil, err + } } if err := t.WriteMsg(q); err != nil { return nil, err @@ -91,7 +103,6 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) { c <- &Envelope{in.Answer, nil} } } - panic("dns: not reached") } func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { @@ -107,7 +118,7 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { t.SetReadDeadline(time.Now().Add(timeout)) in, err := t.ReadMsg() if err != nil { - c <- &Envelope{in.Answer, err} + c <- &Envelope{nil, err} return } if id != in.Id { @@ -160,22 +171,18 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { // The server is responsible for sending the correct sequence of RRs through the // channel ch. func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { - r := new(Msg) - // Compress? - r.SetReply(q) - r.Authoritative = true - - go func() { - for x := range ch { - // assume it fits TODO(miek): fix - r.Answer = append(r.Answer, x.RR...) - if err := w.WriteMsg(r); err != nil { - return - } + for x := range ch { + r := new(Msg) + // Compress? + r.SetReply(q) + r.Authoritative = true + // assume it fits TODO(miek): fix + r.Answer = append(r.Answer, x.RR...) + if err := w.WriteMsg(r); err != nil { + return err } - w.TsigTimersOnly(true) - r.Answer = nil - }() + } + w.TsigTimersOnly(true) return nil } @@ -197,6 +204,7 @@ func (t *Transfer) ReadMsg() (*Msg, error) { } // Need to work on the original message p, as that was used to calculate the tsig. err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly) + t.tsigRequestMAC = ts.MAC } return m, err } diff --git a/vendor/github.com/miekg/dns/zgenerate.go b/vendor/github.com/miekg/dns/zgenerate.go index 7f1183e8740..ce7b540a5d8 100644 --- a/vendor/github.com/miekg/dns/zgenerate.go +++ b/vendor/github.com/miekg/dns/zgenerate.go @@ -1,6 +1,7 @@ package dns import ( + "bytes" "fmt" "strconv" "strings" @@ -14,7 +15,7 @@ import ( // * [[ttl][class]] // * type // * rhs (rdata) -// But we are lazy here, only the range is parsed *all* occurences +// But we are lazy here, only the range is parsed *all* occurrences // of $ after that are interpreted. // Any error are returned as a string value, the empty string signals // "no error". @@ -24,13 +25,13 @@ func generate(l lex, c chan lex, t chan *Token, o string) string { 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, e := strconv.Atoi(l.token[i+1:]); e == nil { if s < 0 { return "bad step in $GENERATE range" } step = s + } else { + return "bad step in $GENERATE range" } l.token = l.token[:i] } @@ -46,7 +47,7 @@ func generate(l lex, c chan lex, t chan *Token, o string) string { if err != nil { return "bad stop in $GENERATE range" } - if end < 0 || start < 0 || end <= start { + if end < 0 || start < 0 || end < start { return "bad range in $GENERATE range" } @@ -55,14 +56,14 @@ func generate(l lex, c chan lex, t chan *Token, o string) string { s := "" BuildRR: l = <-c - if l.value != _NEWLINE && l.value != _EOF { + if l.value != zNewline && l.value != zEOF { s += l.token goto BuildRR } for i := start; i <= end; i += step { var ( escape bool - dom string + dom bytes.Buffer mod string err string offset int @@ -72,7 +73,7 @@ BuildRR: switch s[j] { case '\\': if escape { - dom += "\\" + dom.WriteByte('\\') escape = false continue } @@ -81,17 +82,17 @@ BuildRR: mod = "%d" offset = 0 if escape { - dom += "$" + dom.WriteByte('$') escape = false continue } escape = false if j+1 >= len(s) { // End of the string - dom += fmt.Sprintf(mod, i+offset) + dom.WriteString(fmt.Sprintf(mod, i+offset)) continue } else { if s[j+1] == '$' { - dom += "$" + dom.WriteByte('$') j++ continue } @@ -108,17 +109,17 @@ BuildRR: } j += 2 + sep // Jump to it } - dom += fmt.Sprintf(mod, i+offset) + dom.WriteString(fmt.Sprintf(mod, i+offset)) default: if escape { // Pretty useless here escape = false continue } - dom += string(s[j]) + dom.WriteByte(s[j]) } } // Re-parse the RR and send it on the current channel t - rx, e := NewRR("$ORIGIN " + o + "\n" + dom) + rx, e := NewRR("$ORIGIN " + o + "\n" + dom.String()) if e != nil { return e.(*ParseError).err } @@ -140,11 +141,11 @@ func modToPrintf(s string) (string, int, string) { return "", 0, "bad base in $GENERATE" } offset, err := strconv.Atoi(xs[0]) - if err != nil { + if err != nil || offset > 255 { return "", 0, "bad offset in $GENERATE" } width, err := strconv.Atoi(xs[1]) - if err != nil { + if err != nil || width > 255 { return "", offset, "bad width in $GENERATE" } switch { diff --git a/vendor/github.com/miekg/dns/zscan.go b/vendor/github.com/miekg/dns/zscan.go index 7ba51986953..5c10fbaaaaf 100644 --- a/vendor/github.com/miekg/dns/zscan.go +++ b/vendor/github.com/miekg/dns/zscan.go @@ -29,45 +29,45 @@ const maxUint16 = 1<<16 - 1 // * Handle braces - anywhere. const ( // Zonefile - _EOF = iota - _STRING - _BLANK - _QUOTE - _NEWLINE - _RRTYPE - _OWNER - _CLASS - _DIRORIGIN // $ORIGIN - _DIRTTL // $TTL - _DIRINCLUDE // $INCLUDE - _DIRGENERATE // $GENERATE + zEOF = iota + zString + zBlank + zQuote + zNewline + zRrtpe + zOwner + zClass + zDirOrigin // $ORIGIN + zDirTtl // $TTL + zDirInclude // $INCLUDE + zDirGenerate // $GENERATE // Privatekey file - _VALUE - _KEY + zValue + zKey - _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 + zExpectOwnerDir // Ownername + zExpectOwnerBl // Whitespace after the ownername + zExpectAny // Expect rrtype, ttl or class + zExpectAnyNoClass // Expect rrtype or ttl + zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS + zExpectAnyNoTtl // Expect rrtype or class + zExpectAnyNoTtlBl // Whitespace after _EXPECT_ANY_NOTTL + zExpectRrtype // Expect rrtype + zExpectRrtypeBl // Whitespace BEFORE rrtype + zExpectRdata // The first element of the rdata + zExpectDirTtlBl // Space after directive $TTL + zExpectDirTtl // Directive $TTL + zExpectDirOriginBl // Space after directive $ORIGIN + zExpectDirOrigin // Directive $ORIGIN + zExpectDirIncludeBl // Space after directive $INCLUDE + zExpectDirInclude // Directive $INCLUDE + zExpectDirGenerate // Directive $GENERATE + zExpectDirGenerateBl // Space after directive $GENERATE ) // ParseError is a parsing error. It contains the parse error and the location in the io.Reader -// where the error occured. +// where the error occurred. type ParseError struct { file string err string @@ -86,28 +86,32 @@ func (e *ParseError) Error() (s string) { type lex struct { token string // text of the token tokenUpper string // uppercase text of the token - length int // lenght of the token + length int // length of the token err bool // when true, token text has lexer error - value uint8 // value: _STRING, _BLANK, etc. + value uint8 // value: zString, _BLANK, etc. line int // line in the file column int // column in the file torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar comment string // any comment text seen } -// *Tokens are returned when a zone file is parsed. +// Token holds the token that 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 + // The scanned resource record when error is not nil. + RR + // When an error occurred, this has the error specifics. + Error *ParseError + // A potential comment positioned after the RR and on the same line. + Comment string } -// 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. -// All fields of the returned RR are set, except RR.Header().Rdlength which is set to 0. +// NewRR reads the RR contained in the string s. Only the first RR is +// returned. If s contains no RR, return nil with no error. The class +// defaults to IN and TTL defaults to 3600. The full zone file syntax +// like $TTL, $ORIGIN, etc. is supported. All fields of the returned +// RR are set, except RR.Header().Rdlength which is set to 0. func NewRR(s string) (RR, error) { - if s[len(s)-1] != '\n' { // We need a closing newline + if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline return ReadRR(strings.NewReader(s+"\n"), "") } return ReadRR(strings.NewReader(s), "") @@ -117,17 +121,21 @@ func NewRR(s string) (RR, error) { // See NewRR for more documentation. func ReadRR(q io.Reader, filename string) (RR, error) { r := <-parseZoneHelper(q, ".", filename, 1) + if r == nil { + return nil, nil + } + if r.Error != nil { return nil, r.Error } return r.RR, nil } -// ParseZone reads a RFC 1035 style one from r. It returns *Tokens on the +// ParseZone reads a RFC 1035 style zonefile from r. It returns *Tokens on the // returned channel, which consist out the parsed RR, a potential comment or an error. // If there is an error the RR is nil. The string file is only used // in error reporting. The string origin is used as the initial origin, as -// if the file would start with: $ORIGIN origin . +// 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. // @@ -136,15 +144,17 @@ func ReadRR(q io.Reader, filename string) (RR, error) { // // for x := range dns.ParseZone(strings.NewReader(z), "", "") { // if x.Error != nil { -// // Do something with x.RR -// } +// // log.Println(x.Error) +// } else { +// // Do something with x.RR +// } // } // // Comments specified after an RR (and on the same line!) are returned too: // // foo. IN A 10.0.0.1 ; this is a comment // -// The text "; this is comment" is returned in Token.Comment . Comments inside the +// 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) @@ -163,17 +173,17 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { } }() s := scanInit(r) - c := make(chan lex, 1000) + c := make(chan lex) // 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 + // 0. zRRTYPE -> all omitted until the rrtype + // 1. zOwner _ zRrtype -> class/ttl omitted + // 2. zOwner _ zString _ zRrtype -> class omitted + // 3. zOwner _ zString _ zClass _ zRrtype -> ttl/class + // 4. zOwner _ zClass _ zRrtype -> ttl omitted + // 5. zOwner _ zClass _ zString _ zRrtype -> class/ttl (reversed) + // After detecting these, we know the zRrtype so we can jump to functions // handling the rdata for each of these types. if origin == "" { @@ -185,7 +195,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { return } - st := _EXPECT_OWNER_DIR // initial state + st := zExpectOwnerDir // initial state var h RR_Header var defttl uint32 = defaultTtl var prevName string @@ -197,19 +207,19 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { } switch st { - case _EXPECT_OWNER_DIR: + case zExpectOwnerDir: // 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: + case zNewline: + st = zExpectOwnerDir + case zOwner: h.Name = l.token if l.token[0] == '@' { h.Name = origin prevName = h.Name - st = _EXPECT_OWNER_BL + st = zExpectOwnerBl break } if h.Name[l.length-1] != '.' { @@ -221,59 +231,59 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { 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 + st = zExpectOwnerBl + case zDirTtl: + st = zExpectDirTtlBl + case zDirOrigin: + st = zExpectDirOriginBl + case zDirInclude: + st = zExpectDirIncludeBl + case zDirGenerate: + st = zExpectDirGenerateBl + case zRrtpe: h.Name = prevName h.Rrtype = l.torc - st = _EXPECT_RDATA - case _CLASS: // First thing on the line is the class + st = zExpectRdata + case zClass: h.Name = prevName h.Class = l.torc - st = _EXPECT_ANY_NOCLASS_BL - case _BLANK: + st = zExpectAnyNoClassBl + case zBlank: // 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 { + case zString: + ttl, ok := stringToTtl(l.token) + if !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 + h.Ttl = ttl + // Don't about the defttl, we should take the $TTL value + // defttl = ttl + st = zExpectAnyNoTtlBl default: t <- &Token{Error: &ParseError{f, "syntax error at beginning", l}} return } - case _EXPECT_DIRINCLUDE_BL: - if l.value != _BLANK { + case zExpectDirIncludeBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank after $INCLUDE-directive", l}} return } - st = _EXPECT_DIRINCLUDE - case _EXPECT_DIRINCLUDE: - if l.value != _STRING { + st = zExpectDirInclude + case zExpectDirInclude: + if l.value != zString { t <- &Token{Error: &ParseError{f, "expecting $INCLUDE value, not this...", l}} return } neworigin := origin // There may be optionally a new origin set after the filename, if not use current one l := <-c switch l.value { - case _BLANK: + case zBlank: l := <-c - if l.value == _STRING { - if _, ok := IsDomainName(l.token); !ok { + if l.value == zString { + if _, ok := IsDomainName(l.token); !ok || l.length == 0 || l.err { t <- &Token{Error: &ParseError{f, "bad origin name", l}} return } @@ -288,7 +298,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { neworigin = l.token } } - case _NEWLINE, _EOF: + case zNewline, zEOF: // Ok default: t <- &Token{Error: &ParseError{f, "garbage after $INCLUDE", l}} @@ -305,15 +315,15 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { return } parseZone(r1, l.token, neworigin, t, include+1) - st = _EXPECT_OWNER_DIR - case _EXPECT_DIRTTL_BL: - if l.value != _BLANK { + st = zExpectOwnerDir + case zExpectDirTtlBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank after $TTL-directive", l}} return } - st = _EXPECT_DIRTTL - case _EXPECT_DIRTTL: - if l.value != _STRING { + st = zExpectDirTtl + case zExpectDirTtl: + if l.value != zString { t <- &Token{Error: &ParseError{f, "expecting $TTL value, not this...", l}} return } @@ -321,21 +331,21 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { t <- &Token{Error: e} return } - if ttl, ok := stringToTtl(l.token); !ok { + ttl, ok := stringToTtl(l.token) + if !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 { + defttl = ttl + st = zExpectOwnerDir + case zExpectDirOriginBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank after $ORIGIN-directive", l}} return } - st = _EXPECT_DIRORIGIN - case _EXPECT_DIRORIGIN: - if l.value != _STRING { + st = zExpectDirOrigin + case zExpectDirOrigin: + if l.value != zString { t <- &Token{Error: &ParseError{f, "expecting $ORIGIN value, not this...", l}} return } @@ -355,15 +365,15 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { } else { origin = l.token } - st = _EXPECT_OWNER_DIR - case _EXPECT_DIRGENERATE_BL: - if l.value != _BLANK { + st = zExpectOwnerDir + case zExpectDirGenerateBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank after $GENERATE-directive", l}} return } - st = _EXPECT_DIRGENERATE - case _EXPECT_DIRGENERATE: - if l.value != _STRING { + st = zExpectDirGenerate + case zExpectDirGenerate: + if l.value != zString { t <- &Token{Error: &ParseError{f, "expecting $GENERATE value, not this...", l}} return } @@ -371,90 +381,90 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { t <- &Token{Error: &ParseError{f, e, l}} return } - st = _EXPECT_OWNER_DIR - case _EXPECT_OWNER_BL: - if l.value != _BLANK { + st = zExpectOwnerDir + case zExpectOwnerBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank after owner", l}} return } - st = _EXPECT_ANY - case _EXPECT_ANY: + st = zExpectAny + case zExpectAny: switch l.value { - case _RRTYPE: + case zRrtpe: h.Rrtype = l.torc - st = _EXPECT_RDATA - case _CLASS: + st = zExpectRdata + case zClass: h.Class = l.torc - st = _EXPECT_ANY_NOCLASS_BL - case _STRING: // TTL is this case - if ttl, ok := stringToTtl(l.token); !ok { + st = zExpectAnyNoClassBl + case zString: + ttl, ok := stringToTtl(l.token) + if !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 + h.Ttl = ttl + // defttl = ttl // don't set the defttl here + st = zExpectAnyNoTtlBl default: t <- &Token{Error: &ParseError{f, "expecting RR type, TTL or class, not this...", l}} return } - case _EXPECT_ANY_NOCLASS_BL: - if l.value != _BLANK { + case zExpectAnyNoClassBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank before class", l}} return } - st = _EXPECT_ANY_NOCLASS - case _EXPECT_ANY_NOTTL_BL: - if l.value != _BLANK { + st = zExpectAnyNoClass + case zExpectAnyNoTtlBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank before TTL", l}} return } - st = _EXPECT_ANY_NOTTL - case _EXPECT_ANY_NOTTL: + st = zExpectAnyNoTtl + case zExpectAnyNoTtl: switch l.value { - case _CLASS: + case zClass: h.Class = l.torc - st = _EXPECT_RRTYPE_BL - case _RRTYPE: + st = zExpectRrtypeBl + case zRrtpe: h.Rrtype = l.torc - st = _EXPECT_RDATA + st = zExpectRdata default: t <- &Token{Error: &ParseError{f, "expecting RR type or class, not this...", l}} return } - case _EXPECT_ANY_NOCLASS: + case zExpectAnyNoClass: switch l.value { - case _STRING: // TTL - if ttl, ok := stringToTtl(l.token); !ok { + case zString: + ttl, ok := stringToTtl(l.token) + if !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.Ttl = ttl + // defttl = ttl // don't set the def ttl anymore + st = zExpectRrtypeBl + case zRrtpe: h.Rrtype = l.torc - st = _EXPECT_RDATA + st = zExpectRdata default: t <- &Token{Error: &ParseError{f, "expecting RR type or TTL, not this...", l}} return } - case _EXPECT_RRTYPE_BL: - if l.value != _BLANK { + case zExpectRrtypeBl: + if l.value != zBlank { t <- &Token{Error: &ParseError{f, "no blank before RR type", l}} return } - st = _EXPECT_RRTYPE - case _EXPECT_RRTYPE: - if l.value != _RRTYPE { + st = zExpectRrtype + case zExpectRrtype: + if l.value != zRrtpe { t <- &Token{Error: &ParseError{f, "unknown RR type", l}} return } h.Rrtype = l.torc - st = _EXPECT_RDATA - case _EXPECT_RDATA: + st = zExpectRdata + case zExpectRdata: r, e, c1 := setRR(h, c, origin, f) if e != nil { // If e.lex is nil than we have encounter a unknown RR type @@ -466,7 +476,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) { return } t <- &Token{RR: r, Comment: c1} - st = _EXPECT_OWNER_DIR + st = zExpectOwnerDir } } // If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this @@ -492,14 +502,14 @@ func zlexer(s *scan, c chan lex) { for err == nil { l.column = s.position.Column l.line = s.position.Line - if stri > maxTok { + if stri >= maxTok { l.token = "token length insufficient for parsing" l.err = true debug.Printf("[%+v]", l.token) c <- l return } - if comi > maxTok { + if comi >= maxTok { l.token = "comment length insufficient for parsing" l.err = true debug.Printf("[%+v]", l.token) @@ -530,60 +540,60 @@ func zlexer(s *scan, c chan lex) { // 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.value = zOwner l.token = string(str[:stri]) l.tokenUpper = strings.ToUpper(l.token) l.length = stri // escape $... start with a \ not a $, so this will work switch l.tokenUpper { case "$TTL": - l.value = _DIRTTL + l.value = zDirTtl case "$ORIGIN": - l.value = _DIRORIGIN + l.value = zDirOrigin case "$INCLUDE": - l.value = _DIRINCLUDE + l.value = zDirInclude case "$GENERATE": - l.value = _DIRGENERATE + l.value = zDirGenerate } debug.Printf("[7 %+v]", l.token) c <- l } else { - l.value = _STRING + l.value = zString l.token = string(str[:stri]) l.tokenUpper = strings.ToUpper(l.token) l.length = stri if !rrtype { if t, ok := StringToType[l.tokenUpper]; ok { - l.value = _RRTYPE + l.value = zRrtpe l.torc = t rrtype = true } else { if strings.HasPrefix(l.tokenUpper, "TYPE") { - if t, ok := typeToInt(l.token); !ok { + t, ok := typeToInt(l.token) + if !ok { l.token = "unknown RR type" l.err = true c <- l return - } else { - l.value = _RRTYPE - l.torc = t } + l.value = zRrtpe + l.torc = t } } if t, ok := StringToClass[l.tokenUpper]; ok { - l.value = _CLASS + l.value = zClass l.torc = t } else { if strings.HasPrefix(l.tokenUpper, "CLASS") { - if t, ok := classToInt(l.token); !ok { + t, ok := classToInt(l.token) + if !ok { l.token = "unknown class" l.err = true c <- l return - } else { - l.value = _CLASS - l.torc = t } + l.value = zClass + l.torc = t } } } @@ -593,7 +603,7 @@ func zlexer(s *scan, c chan lex) { stri = 0 // I reverse space stuff here if !space && !commt { - l.value = _BLANK + l.value = zBlank l.token = " " l.length = 1 debug.Printf("[5 %+v]", l.token) @@ -615,7 +625,7 @@ func zlexer(s *scan, c chan lex) { break } if stri > 0 { - l.value = _STRING + l.value = zString l.token = string(str[:stri]) l.length = stri debug.Printf("[4 %+v]", l.token) @@ -651,7 +661,7 @@ func zlexer(s *scan, c chan lex) { if brace == 0 { owner = true owner = true - l.value = _NEWLINE + l.value = zNewline l.token = "\n" l.length = 1 l.comment = string(com[:comi]) @@ -669,14 +679,14 @@ func zlexer(s *scan, c chan lex) { if brace == 0 { // If there is previous text, we should output it here if stri != 0 { - l.value = _STRING + l.value = zString l.token = string(str[:stri]) l.tokenUpper = strings.ToUpper(l.token) l.length = stri if !rrtype { if t, ok := StringToType[l.tokenUpper]; ok { - l.value = _RRTYPE + l.value = zRrtpe l.torc = t rrtype = true } @@ -684,7 +694,7 @@ func zlexer(s *scan, c chan lex) { debug.Printf("[2 %+v]", l.token) c <- l } - l.value = _NEWLINE + l.value = zNewline l.token = "\n" l.length = 1 debug.Printf("[1 %+v]", l.token) @@ -728,7 +738,7 @@ func zlexer(s *scan, c chan lex) { space = false // send previous gathered text and the quote if stri != 0 { - l.value = _STRING + l.value = zString l.token = string(str[:stri]) l.length = stri @@ -738,7 +748,7 @@ func zlexer(s *scan, c chan lex) { } // send quote itself as separate token - l.value = _QUOTE + l.value = zQuote l.token = "\"" l.length = 1 c <- l @@ -790,7 +800,7 @@ func zlexer(s *scan, c chan lex) { // Send remainder l.token = string(str[:stri]) l.length = stri - l.value = _STRING + l.value = zString debug.Printf("[%+v]", l.token) c <- l } @@ -798,7 +808,11 @@ func zlexer(s *scan, c chan lex) { // Extract the class number from CLASSxx func classToInt(token string) (uint16, bool) { - class, ok := strconv.Atoi(token[5:]) + offset := 5 + if len(token) < offset+1 { + return 0, false + } + class, ok := strconv.Atoi(token[offset:]) if ok != nil || class > maxUint16 { return 0, false } @@ -807,7 +821,11 @@ func classToInt(token string) (uint16, bool) { // Extract the rr number from TYPExxx func typeToInt(token string) (uint16, bool) { - typ, ok := strconv.Atoi(token[4:]) + offset := 4 + if len(token) < offset+1 { + return 0, false + } + typ, ok := strconv.Atoi(token[offset:]) if ok != nil || typ > maxUint16 { return 0, false } @@ -922,15 +940,15 @@ func slurpRemainder(c chan lex, f string) (*ParseError, string) { l := <-c com := "" switch l.value { - case _BLANK: + case zBlank: l = <-c com = l.comment - if l.value != _NEWLINE && l.value != _EOF { + if l.value != zNewline && l.value != zEOF { return &ParseError{f, "garbage after rdata", l}, "" } - case _NEWLINE: + case zNewline: com = l.comment - case _EOF: + case zEOF: default: return &ParseError{f, "garbage after rdata", l}, "" } diff --git a/vendor/github.com/miekg/dns/zscan_rr.go b/vendor/github.com/miekg/dns/zscan_rr.go index 5088cdb58e2..ead9614d13b 100644 --- a/vendor/github.com/miekg/dns/zscan_rr.go +++ b/vendor/github.com/miekg/dns/zscan_rr.go @@ -19,9 +19,9 @@ type parserFunc struct { } // 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 +// All data from the channel c is either zString or zBlank. +// After the rdata there may come a zBlank and then a zNewline +// or immediately a zNewline. If this is not the case we flag // an *ParseError: garbage after rdata. func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { parserfunc, ok := typeToparserFunc[h.Rrtype] @@ -47,12 +47,15 @@ func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { // 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 { + l := <-c // zString + for l.value != zNewline && l.value != zEOF { + if l.err { + return s, &ParseError{f, errstr, l}, "" + } switch l.value { - case _STRING: + case zString: s += l.token - case _BLANK: // Ok + case zBlank: // Ok default: return "", &ParseError{f, errstr, l}, "" } @@ -64,25 +67,49 @@ func endingToString(c chan lex, errstr, f string) (string, *ParseError, string) // 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 + // Get the remaining data until we see a zNewline quote := false l := <-c var s []string - switch l.value == _QUOTE { + if l.err { + return s, &ParseError{f, errstr, l}, "" + } + switch l.value == zQuote { case true: // A number of quoted string s = make([]string, 0) empty := true - for l.value != _NEWLINE && l.value != _EOF { + for l.value != zNewline && l.value != zEOF { + if l.err { + return nil, &ParseError{f, errstr, l}, "" + } switch l.value { - case _STRING: + case zString: empty = false + if len(l.token) > 255 { + // split up tokens that are larger than 255 into 255-chunks + sx := []string{} + p, i := 0, 255 + for { + if i <= len(l.token) { + sx = append(sx, l.token[p:i]) + } else { + sx = append(sx, l.token[p:]) + break + + } + p, i = p+255, i+255 + } + s = append(s, sx...) + break + } + s = append(s, l.token) - case _BLANK: + case zBlank: if quote { - // _BLANK can only be seen in between txt parts. + // zBlank can only be seen in between txt parts. return nil, &ParseError{f, errstr, l}, "" } - case _QUOTE: + case zQuote: if empty && quote { s = append(s, "") } @@ -98,7 +125,10 @@ func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, stri } case false: // Unquoted text record s = make([]string, 1) - for l.value != _NEWLINE && l.value != _EOF { + for l.value != zNewline && l.value != zEOF { + if l.err { + return s, &ParseError{f, errstr, l}, "" + } s[0] += l.token l = <-c } @@ -115,7 +145,7 @@ func setA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } rr.A = net.ParseIP(l.token) - if rr.A == nil { + if rr.A == nil || l.err { return nil, &ParseError{f, "bad A A", l}, "" } return rr, nil, "" @@ -130,7 +160,7 @@ func setAAAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } rr.AAAA = net.ParseIP(l.token) - if rr.AAAA == nil { + if rr.AAAA == nil || l.err { return nil, &ParseError{f, "bad AAAA AAAA", l}, "" } return rr, nil, "" @@ -150,7 +180,7 @@ func setNS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad NS Ns", l}, "" } if rr.Ns[l.length-1] != '.' { @@ -173,7 +203,7 @@ func setPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad PTR Ptr", l}, "" } if rr.Ptr[l.length-1] != '.' { @@ -196,7 +226,7 @@ func setNSAPPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad NSAP-PTR Ptr", l}, "" } if rr.Ptr[l.length-1] != '.' { @@ -218,14 +248,14 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Mbox = o } else { _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad RP Mbox", l}, "" } if rr.Mbox[l.length-1] != '.' { rr.Mbox = appendOrigin(rr.Mbox, o) } } - <-c // _BLANK + <-c // zBlank l = <-c rr.Txt = l.token if l.token == "@" { @@ -233,7 +263,7 @@ func setRP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad RP Txt", l}, "" } if rr.Txt[l.length-1] != '.' { @@ -256,7 +286,7 @@ func setMR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MR Mr", l}, "" } if rr.Mr[l.length-1] != '.' { @@ -279,7 +309,7 @@ func setMB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MB Mb", l}, "" } if rr.Mb[l.length-1] != '.' { @@ -302,7 +332,7 @@ func setMG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MG Mg", l}, "" } if rr.Mg[l.length-1] != '.' { @@ -315,11 +345,24 @@ func setHINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(HINFO) rr.Hdr = h - l := <-c - rr.Cpu = l.token - <-c // _BLANK - l = <-c // _STRING - rr.Os = l.token + chunks, e, c1 := endingToTxtSlice(c, "bad HINFO Fields", f) + if e != nil { + return nil, e, c1 + } + + if ln := len(chunks); ln == 0 { + return rr, nil, "" + } else if ln == 1 { + // Can we split it? + if out := strings.Fields(chunks[0]); len(out) > 1 { + chunks = out + } else { + chunks = append(chunks, "") + } + } + + rr.Cpu = chunks[0] + rr.Os = strings.Join(chunks[1:], " ") return rr, nil, "" } @@ -337,14 +380,14 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Rmail = o } else { _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MINFO Rmail", l}, "" } if rr.Rmail[l.length-1] != '.' { rr.Rmail = appendOrigin(rr.Rmail, o) } } - <-c // _BLANK + <-c // zBlank l = <-c rr.Email = l.token if l.token == "@" { @@ -352,7 +395,7 @@ func setMINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MINFO Email", l}, "" } if rr.Email[l.length-1] != '.' { @@ -375,7 +418,7 @@ func setMF(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MF Mf", l}, "" } if rr.Mf[l.length-1] != '.' { @@ -398,7 +441,7 @@ func setMD(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MD Md", l}, "" } if rr.Md[l.length-1] != '.' { @@ -415,20 +458,20 @@ func setMX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad MX Pref", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString rr.Mx = l.token if l.token == "@" { rr.Mx = o return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad MX Mx", l}, "" } if rr.Mx[l.length-1] != '.' { @@ -444,20 +487,20 @@ func setRT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil { return nil, &ParseError{f, "bad RT Preference", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString rr.Host = l.token if l.token == "@" { rr.Host = o return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad RT Host", l}, "" } if rr.Host[l.length-1] != '.' { @@ -474,20 +517,20 @@ func setAFSDB(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad AFSDB Subtype", l}, "" - } else { - rr.Subtype = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Subtype = uint16(i) + <-c // zBlank + l = <-c // zString rr.Hostname = l.token if l.token == "@" { rr.Hostname = o return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad AFSDB Hostname", l}, "" } if rr.Hostname[l.length-1] != '.' { @@ -501,6 +544,12 @@ func setX25(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Hdr = h l := <-c + if l.length == 0 { + return rr, nil, "" + } + if l.err { + return nil, &ParseError{f, "bad X25 PSDNAddress", l}, "" + } rr.PSDNAddress = l.token return rr, nil, "" } @@ -513,20 +562,20 @@ func setKX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad KX Pref", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString rr.Exchanger = l.token if l.token == "@" { rr.Exchanger = o return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad KX Exchanger", l}, "" } if rr.Exchanger[l.length-1] != '.' { @@ -549,7 +598,7 @@ func setCNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad CNAME Target", l}, "" } if rr.Target[l.length-1] != '.' { @@ -572,7 +621,7 @@ func setDNAME(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad CNAME Target", l}, "" } if rr.Target[l.length-1] != '.' { @@ -590,12 +639,12 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - <-c // _BLANK + <-c // zBlank if l.token == "@" { rr.Ns = o } else { _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad SOA Ns", l}, "" } if rr.Ns[l.length-1] != '.' { @@ -609,14 +658,14 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Mbox = o } else { _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad SOA Mbox", l}, "" } if rr.Mbox[l.length-1] != '.' { rr.Mbox = appendOrigin(rr.Mbox, o) } } - <-c // _BLANK + <-c // zBlank var ( v uint32 @@ -624,6 +673,9 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { ) for i := 0; i < 5; i++ { l = <-c + if l.err { + return nil, &ParseError{f, "bad SOA zone parameter", l}, "" + } if j, e := strconv.Atoi(l.token); e != nil { if i == 0 { // Serial should be a number @@ -639,16 +691,16 @@ func setSOA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { switch i { case 0: rr.Serial = v - <-c // _BLANK + <-c // zBlank case 1: rr.Refresh = v - <-c // _BLANK + <-c // zBlank case 2: rr.Retry = v - <-c // _BLANK + <-c // zBlank case 3: rr.Expire = v - <-c // _BLANK + <-c // zBlank case 4: rr.Minttl = v } @@ -664,34 +716,34 @@ func setSRV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { 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 { + rr.Priority = uint16(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad SRV Weight", l}, "" - } else { - rr.Weight = uint16(i) } - <-c // _BLANK - l = <-c // _STRING - if i, e := strconv.Atoi(l.token); e != nil { + rr.Weight = uint16(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad SRV Port", l}, "" - } else { - rr.Port = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Port = uint16(i) + <-c // zBlank + l = <-c // zString rr.Target = l.token if l.token == "@" { rr.Target = o return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad SRV Target", l}, "" } if rr.Target[l.length-1] != '.' { @@ -708,84 +760,84 @@ func setNAPTR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { 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 { + rr.Order = uint16(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NAPTR Preference", l}, "" - } else { - rr.Preference = uint16(i) } + rr.Preference = uint16(i) // Flags - <-c // _BLANK + <-c // zBlank l = <-c // _QUOTE - if l.value != _QUOTE { + if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Flags", l}, "" } l = <-c // Either String or Quote - if l.value == _STRING { + if l.value == zString { rr.Flags = l.token l = <-c // _QUOTE - if l.value != _QUOTE { + if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Flags", l}, "" } - } else if l.value == _QUOTE { + } else if l.value == zQuote { rr.Flags = "" } else { return nil, &ParseError{f, "bad NAPTR Flags", l}, "" } // Service - <-c // _BLANK + <-c // zBlank l = <-c // _QUOTE - if l.value != _QUOTE { + if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Service", l}, "" } l = <-c // Either String or Quote - if l.value == _STRING { + if l.value == zString { rr.Service = l.token l = <-c // _QUOTE - if l.value != _QUOTE { + if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Service", l}, "" } - } else if l.value == _QUOTE { + } else if l.value == zQuote { rr.Service = "" } else { return nil, &ParseError{f, "bad NAPTR Service", l}, "" } // Regexp - <-c // _BLANK + <-c // zBlank l = <-c // _QUOTE - if l.value != _QUOTE { + if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Regexp", l}, "" } l = <-c // Either String or Quote - if l.value == _STRING { + if l.value == zString { rr.Regexp = l.token l = <-c // _QUOTE - if l.value != _QUOTE { + if l.value != zQuote { return nil, &ParseError{f, "bad NAPTR Regexp", l}, "" } - } else if l.value == _QUOTE { + } else if l.value == zQuote { rr.Regexp = "" } else { return nil, &ParseError{f, "bad NAPTR Regexp", l}, "" } // After quote no space?? - <-c // _BLANK - l = <-c // _STRING + <-c // zBlank + l = <-c // zString rr.Replacement = l.token if l.token == "@" { rr.Replacement = o return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad NAPTR Replacement", l}, "" } if rr.Replacement[l.length-1] != '.' { @@ -807,14 +859,14 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.PreviousName = o } else { _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad TALINK PreviousName", l}, "" } if rr.PreviousName[l.length-1] != '.' { rr.PreviousName = appendOrigin(rr.PreviousName, o) } } - <-c // _BLANK + <-c // zBlank l = <-c rr.NextName = l.token if l.token == "@" { @@ -822,7 +874,7 @@ func setTALINK(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad TALINK NextName", l}, "" } if rr.NextName[l.length-1] != '.' { @@ -844,30 +896,32 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad LOC Latitude", l}, "" - } else { - rr.Latitude = 1000 * 60 * 60 * uint32(i) } - <-c // _BLANK + rr.Latitude = 1000 * 60 * 60 * uint32(i) + + <-c // zBlank // Either number, 'N' or 'S' l = <-c if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad LOC Latitude minutes", l}, "" - } else { - rr.Latitude += 1000 * 60 * uint32(i) } - <-c // _BLANK + rr.Latitude += 1000 * 60 * uint32(i) + + <-c // zBlank l = <-c - if i, e := strconv.ParseFloat(l.token, 32); e != nil { + if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { return nil, &ParseError{f, "bad LOC Latitude seconds", l}, "" } else { rr.Latitude += uint32(1000 * i) } - <-c // _BLANK + <-c // zBlank // Either number, 'N' or 'S' l = <-c if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { @@ -878,32 +932,32 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { East: // East - <-c // _BLANK + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + if i, e := strconv.Atoi(l.token); e != nil || l.err { return nil, &ParseError{f, "bad LOC Longitude", l}, "" } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) } - <-c // _BLANK + <-c // zBlank // Either number, 'E' or 'W' l = <-c if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } - if i, e := strconv.Atoi(l.token); e != nil { + if i, e := strconv.Atoi(l.token); e != nil || l.err { return nil, &ParseError{f, "bad LOC Longitude minutes", l}, "" } else { rr.Longitude += 1000 * 60 * uint32(i) } - <-c // _BLANK + <-c // zBlank l = <-c - if i, e := strconv.ParseFloat(l.token, 32); e != nil { + if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { return nil, &ParseError{f, "bad LOC Longitude seconds", l}, "" } else { rr.Longitude += uint32(1000 * i) } - <-c // _BLANK + <-c // zBlank // Either number, 'E' or 'W' l = <-c if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { @@ -913,8 +967,11 @@ East: return nil, &ParseError{f, "bad LOC Longitude East/West", l}, "" Altitude: - <-c // _BLANK + <-c // zBlank l = <-c + if l.length == 0 || l.err { + return nil, &ParseError{f, "bad LOC Altitude", l}, "" + } if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { l.token = l.token[0 : len(l.token)-1] } @@ -927,31 +984,31 @@ Altitude: // And now optionally the other values l = <-c count := 0 - for l.value != _NEWLINE && l.value != _EOF { + for l.value != zNewline && l.value != zEOF { switch l.value { - case _STRING: + case zString: switch count { case 0: // Size - if e, m, ok := stringToCm(l.token); !ok { + e, m, ok := stringToCm(l.token) + if !ok { return nil, &ParseError{f, "bad LOC Size", l}, "" - } else { - rr.Size = (e & 0x0f) | (m << 4 & 0xf0) } + rr.Size = (e & 0x0f) | (m << 4 & 0xf0) case 1: // HorizPre - if e, m, ok := stringToCm(l.token); !ok { + e, m, ok := stringToCm(l.token) + if !ok { return nil, &ParseError{f, "bad LOC HorizPre", l}, "" - } else { - rr.HorizPre = (e & 0x0f) | (m << 4 & 0xf0) } + rr.HorizPre = (e & 0x0f) | (m << 4 & 0xf0) case 2: // VertPre - if e, m, ok := stringToCm(l.token); !ok { + e, m, ok := stringToCm(l.token) + if !ok { return nil, &ParseError{f, "bad LOC VertPre", l}, "" - } else { - rr.VertPre = (e & 0x0f) | (m << 4 & 0xf0) } + rr.VertPre = (e & 0x0f) | (m << 4 & 0xf0) } count++ - case _BLANK: + case zBlank: // Ok default: return nil, &ParseError{f, "bad LOC Size, HorizPre or VertPre", l}, "" @@ -970,40 +1027,47 @@ func setHIP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad HIP PublicKeyAlgorithm", l}, "" - } else { - rr.PublicKeyAlgorithm = uint8(i) } - <-c // _BLANK - l = <-c // _STRING + rr.PublicKeyAlgorithm = uint8(i) + <-c // zBlank + l = <-c // zString + if l.length == 0 || l.err { + return nil, &ParseError{f, "bad HIP Hit", l}, "" + } rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. rr.HitLength = uint8(len(rr.Hit)) / 2 - <-c // _BLANK - l = <-c // _STRING + <-c // zBlank + l = <-c // zString + if l.length == 0 || l.err { + return nil, &ParseError{f, "bad HIP PublicKey", l}, "" + } rr.PublicKey = l.token // This cannot contain spaces rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey))) // RendezvousServers (if any) l = <-c - xs := make([]string, 0) - for l.value != _NEWLINE && l.value != _EOF { + var xs []string + for l.value != zNewline && l.value != zEOF { switch l.value { - case _STRING: + case zString: if l.token == "@" { xs = append(xs, o) + l = <-c continue } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" } if l.token[l.length-1] != '.' { l.token = appendOrigin(l.token, o) } xs = append(xs, l.token) - case _BLANK: + case zBlank: // Ok default: return nil, &ParseError{f, "bad HIP RendezvousServers", l}, "" @@ -1029,15 +1093,15 @@ func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } else { rr.Type = uint16(i) } - <-c // _BLANK - l = <-c // _STRING - if i, e := strconv.Atoi(l.token); e != nil { + <-c // zBlank + l = <-c // zString + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad CERT KeyTag", l}, "" - } else { - rr.KeyTag = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.KeyTag = uint16(i) + <-c // zBlank + l = <-c // zString if v, ok := StringToAlgorithm[l.token]; ok { rr.Algorithm = v } else if i, e := strconv.Atoi(l.token); e != nil { @@ -1045,9 +1109,9 @@ func setCERT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } else { rr.Algorithm = uint8(i) } - s, e, c1 := endingToString(c, "bad CERT Certificate", f) - if e != nil { - return nil, e, c1 + s, e1, c1 := endingToString(c, "bad CERT Certificate", f) + if e1 != nil { + return nil, e1, c1 } rr.Certificate = s return rr, nil, c1 @@ -1082,39 +1146,39 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } if t, ok := StringToType[l.tokenUpper]; !ok { if strings.HasPrefix(l.tokenUpper, "TYPE") { - if t, ok = typeToInt(l.tokenUpper); !ok { + t, ok = typeToInt(l.tokenUpper) + if !ok { return nil, &ParseError{f, "bad RRSIG Typecovered", l}, "" - } else { - rr.TypeCovered = t } + rr.TypeCovered = t } else { return nil, &ParseError{f, "bad RRSIG Typecovered", l}, "" } } else { rr.TypeCovered = t } - <-c // _BLANK + <-c // zBlank l = <-c - if i, err := strconv.Atoi(l.token); err != nil { + i, err := strconv.Atoi(l.token) + if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG Algorithm", l}, "" - } else { - rr.Algorithm = uint8(i) } - <-c // _BLANK + rr.Algorithm = uint8(i) + <-c // zBlank l = <-c - if i, err := strconv.Atoi(l.token); err != nil { + i, err = strconv.Atoi(l.token) + if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG Labels", l}, "" - } else { - rr.Labels = uint8(i) } - <-c // _BLANK + rr.Labels = uint8(i) + <-c // zBlank l = <-c - if i, err := strconv.Atoi(l.token); err != nil { + i, err = strconv.Atoi(l.token) + if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG OrigTtl", l}, "" - } else { - rr.OrigTtl = uint32(i) } - <-c // _BLANK + rr.OrigTtl = uint32(i) + <-c // zBlank l = <-c if i, err := StringToTime(l.token); err != nil { // Try to see if all numeric and use it as epoch @@ -1127,7 +1191,7 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } else { rr.Expiration = i } - <-c // _BLANK + <-c // zBlank l = <-c if i, err := StringToTime(l.token); err != nil { if i, err := strconv.ParseInt(l.token, 10, 64); err == nil { @@ -1138,21 +1202,21 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } else { rr.Inception = i } - <-c // _BLANK + <-c // zBlank l = <-c - if i, err := strconv.Atoi(l.token); err != nil { + i, err = strconv.Atoi(l.token) + if err != nil || l.err { return nil, &ParseError{f, "bad RRSIG KeyTag", l}, "" - } else { - rr.KeyTag = uint16(i) } - <-c // _BLANK + rr.KeyTag = uint16(i) + <-c // zBlank l = <-c rr.SignerName = l.token if l.token == "@" { rr.SignerName = o } else { _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad RRSIG SignerName", l}, "" } if rr.SignerName[l.length-1] != '.' { @@ -1180,7 +1244,7 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.NextDomain = o } else { _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad NSEC NextDomain", l}, "" } if rr.NextDomain[l.length-1] != '.' { @@ -1194,11 +1258,11 @@ func setNSEC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { ok bool ) l = <-c - for l.value != _NEWLINE && l.value != _EOF { + for l.value != zNewline && l.value != zEOF { switch l.value { - case _BLANK: + case zBlank: // Ok - case _STRING: + case zString: if k, ok = StringToType[l.tokenUpper]; !ok { if k, ok = typeToInt(l.tokenUpper); !ok { return nil, &ParseError{f, "bad NSEC TypeBitMap", l}, "" @@ -1221,28 +1285,28 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3 Hash", l}, "" - } else { - rr.Hash = uint8(i) } - <-c // _BLANK + rr.Hash = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3 Flags", l}, "" - } else { - rr.Flags = uint8(i) } - <-c // _BLANK + rr.Flags = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3 Iterations", l}, "" - } else { - rr.Iterations = uint16(i) } + rr.Iterations = uint16(i) <-c l = <-c - if len(l.token) == 0 { + if len(l.token) == 0 || l.err { return nil, &ParseError{f, "bad NSEC3 Salt", l}, "" } rr.SaltLength = uint8(len(l.token)) / 2 @@ -1250,6 +1314,9 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { <-c l = <-c + if len(l.token) == 0 || l.err { + return nil, &ParseError{f, "bad NSEC3 NextDomain", l}, "" + } rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) rr.NextDomain = l.token @@ -1259,11 +1326,11 @@ func setNSEC3(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { ok bool ) l = <-c - for l.value != _NEWLINE && l.value != _EOF { + for l.value != zNewline && l.value != zEOF { switch l.value { - case _BLANK: + case zBlank: // Ok - case _STRING: + case zString: if k, ok = StringToType[l.tokenUpper]; !ok { if k, ok = typeToInt(l.tokenUpper); !ok { return nil, &ParseError{f, "bad NSEC3 TypeBitMap", l}, "" @@ -1286,25 +1353,25 @@ func setNSEC3PARAM(h RR_Header, c chan lex, o, f string) (RR, *ParseError, strin if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3PARAM Hash", l}, "" - } else { - rr.Hash = uint8(i) } - <-c // _BLANK + rr.Hash = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3PARAM Flags", l}, "" - } else { - rr.Flags = uint8(i) } - <-c // _BLANK + rr.Flags = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NSEC3PARAM Iterations", l}, "" - } else { - rr.Iterations = uint16(i) } + rr.Iterations = uint16(i) <-c l = <-c rr.SaltLength = uint8(len(l.token)) @@ -1320,7 +1387,7 @@ func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if l.length != 17 { + if l.length != 17 || l.err { return nil, &ParseError{f, "bad EUI48 Address", l}, "" } addr := make([]byte, 12) @@ -1336,11 +1403,11 @@ func setEUI48(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { addr[10] = l.token[15] addr[11] = l.token[16] - if i, e := strconv.ParseUint(string(addr), 16, 48); e != nil { + i, e := strconv.ParseUint(string(addr), 16, 48) + if e != nil { return nil, &ParseError{f, "bad EUI48 Address", l}, "" - } else { - rr.Address = i } + rr.Address = i return rr, nil, "" } @@ -1352,7 +1419,7 @@ func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if l.length != 23 { + if l.length != 23 || l.err { return nil, &ParseError{f, "bad EUI64 Address", l}, "" } addr := make([]byte, 16) @@ -1368,11 +1435,11 @@ func setEUI64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { addr[14] = l.token[21] addr[15] = l.token[22] - if i, e := strconv.ParseUint(string(addr), 16, 64); e != nil { + i, e := strconv.ParseUint(string(addr), 16, 64) + if e != nil { return nil, &ParseError{f, "bad EUI68 Address", l}, "" - } else { - rr.Address = uint64(i) } + rr.Address = uint64(i) return rr, nil, "" } @@ -1385,25 +1452,25 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, l.comment } rr.Address = net.ParseIP(l.token) - if rr.Address == nil { + if rr.Address == nil || l.err { return nil, &ParseError{f, "bad WKS Address", l}, "" } - <-c // _BLANK + <-c // zBlank l = <-c proto := "tcp" - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { + return nil, &ParseError{f, "bad WKS Protocol", l}, "" + } + rr.Protocol = uint8(i) + switch rr.Protocol { + case 17: + proto = "udp" + case 6: + proto = "tcp" + default: return nil, &ParseError{f, "bad WKS Protocol", l}, "" - } 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 @@ -1413,17 +1480,17 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { k int err error ) - for l.value != _NEWLINE && l.value != _EOF { + for l.value != zNewline && l.value != zEOF { switch l.value { - case _BLANK: + case zBlank: // Ok - case _STRING: + case zString: 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 { + i, e := strconv.Atoi(l.token) // If a number use that + if e != nil { return nil, &ParseError{f, "bad WKS BitMap", l}, "" } + rr.BitMap = append(rr.BitMap, uint16(i)) } rr.BitMap = append(rr.BitMap, uint16(k)) default: @@ -1442,21 +1509,24 @@ func setSSHFP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad SSHFP Algorithm", l}, "" - } else { - rr.Algorithm = uint8(i) } - <-c // _BLANK + rr.Algorithm = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad SSHFP Type", l}, "" - } else { - rr.Type = uint8(i) } - <-c // _BLANK - l = <-c - rr.FingerPrint = l.token + rr.Type = uint8(i) + <-c // zBlank + s, e1, c1 := endingToString(c, "bad SSHFP Fingerprint", f) + if e1 != nil { + return nil, e1, c1 + } + rr.FingerPrint = s return rr, nil, "" } @@ -1468,28 +1538,28 @@ func setDNSKEYs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, str if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " Flags", l}, "" - } else { - rr.Flags = uint16(i) } - <-c // _BLANK - l = <-c // _STRING - if i, e := strconv.Atoi(l.token); e != nil { + rr.Flags = uint16(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " Protocol", l}, "" - } else { - rr.Protocol = uint8(i) } - <-c // _BLANK - l = <-c // _STRING - if i, e := strconv.Atoi(l.token); e != nil { + rr.Protocol = uint8(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, "" - } else { - rr.Algorithm = uint8(i) } - s, e, c1 := endingToString(c, "bad "+typ+" PublicKey", f) - if e != nil { - return nil, e, c1 + rr.Algorithm = uint8(i) + s, e1, c1 := endingToString(c, "bad "+typ+" PublicKey", f) + if e1 != nil { + return nil, e1, c1 } rr.PublicKey = s return rr, nil, c1 @@ -1524,28 +1594,28 @@ func setRKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { 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 { + rr.Flags = uint16(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad RKEY Protocol", l}, "" - } else { - rr.Protocol = uint8(i) } - <-c // _BLANK - l = <-c // _STRING - if i, e := strconv.Atoi(l.token); e != nil { + rr.Protocol = uint8(i) + <-c // zBlank + l = <-c // zString + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad RKEY Algorithm", l}, "" - } else { - rr.Algorithm = uint8(i) } - s, e, c1 := endingToString(c, "bad RKEY PublicKey", f) - if e != nil { - return nil, e, c1 + rr.Algorithm = uint8(i) + s, e1, c1 := endingToString(c, "bad RKEY PublicKey", f) + if e1 != nil { + return nil, e1, c1 } rr.PublicKey = s return rr, nil, c1 @@ -1573,27 +1643,6 @@ func setNIMLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, c1 } -func setNSAP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { - rr := new(NSAP) - rr.Hdr = h - l := <-c - if l.length == 0 { - return rr, nil, l.comment - } - if i, e := strconv.Atoi(l.token); e != nil { - return nil, &ParseError{f, "bad NSAP Length", l}, "" - } else { - rr.Length = uint8(i) - } - <-c // _BLANK - s, e, c1 := endingToString(c, "bad NSAP Nsap", f) - if e != nil { - return nil, e, c1 - } - rr.Nsap = s - return rr, nil, c1 -} - func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(GPOS) rr.Hdr = h @@ -1601,25 +1650,25 @@ func setGPOS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if _, e := strconv.ParseFloat(l.token, 64); e != nil { + _, e := strconv.ParseFloat(l.token, 64) + if e != nil || l.err { return nil, &ParseError{f, "bad GPOS Longitude", l}, "" - } else { - rr.Longitude = l.token } - <-c // _BLANK + rr.Longitude = l.token + <-c // zBlank l = <-c - if _, e := strconv.ParseFloat(l.token, 64); e != nil { + _, e = strconv.ParseFloat(l.token, 64) + if e != nil || l.err { return nil, &ParseError{f, "bad GPOS Latitude", l}, "" - } else { - rr.Latitude = l.token } - <-c // _BLANK + rr.Latitude = l.token + <-c // zBlank l = <-c - if _, e := strconv.ParseFloat(l.token, 64); e != nil { + _, e = strconv.ParseFloat(l.token, 64) + if e != nil || l.err { return nil, &ParseError{f, "bad GPOS Altitude", l}, "" - } else { - rr.Altitude = l.token } + rr.Altitude = l.token return rr, nil, "" } @@ -1630,32 +1679,32 @@ func setDSs(h RR_Header, c chan lex, o, f, typ string) (RR, *ParseError, string) if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " KeyTag", l}, "" - } else { - rr.KeyTag = uint16(i) } - <-c // _BLANK + rr.KeyTag = uint16(i) + <-c // zBlank l = <-c if i, e := strconv.Atoi(l.token); e != nil { - if i, ok := StringToAlgorithm[l.tokenUpper]; !ok { + i, ok := StringToAlgorithm[l.tokenUpper] + if !ok || l.err { return nil, &ParseError{f, "bad " + typ + " Algorithm", l}, "" - } else { - rr.Algorithm = i } + rr.Algorithm = i } else { rr.Algorithm = uint8(i) } - <-c // _BLANK + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad " + typ + " DigestType", l}, "" - } else { - rr.DigestType = uint8(i) } - s, e, c1 := endingToString(c, "bad "+typ+" Digest", f) - if e != nil { - return nil, e, c1 + rr.DigestType = uint8(i) + s, e1, c1 := endingToString(c, "bad "+typ+" Digest", f) + if e1 != nil { + return nil, e1, c1 } rr.Digest = s return rr, nil, c1 @@ -1675,7 +1724,7 @@ func setDLV(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } func setCDS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { - r, e, s := setDSs(h, c, o, f, "DLV") + r, e, s := setDSs(h, c, o, f, "CDS") if r != nil { return &CDS{*r.(*DS)}, e, s } @@ -1689,32 +1738,32 @@ func setTA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad TA KeyTag", l}, "" - } else { - rr.KeyTag = uint16(i) } - <-c // _BLANK + rr.KeyTag = uint16(i) + <-c // zBlank l = <-c if i, e := strconv.Atoi(l.token); e != nil { - if i, ok := StringToAlgorithm[l.tokenUpper]; !ok { + i, ok := StringToAlgorithm[l.tokenUpper] + if !ok || l.err { return nil, &ParseError{f, "bad TA Algorithm", l}, "" - } else { - rr.Algorithm = i } + rr.Algorithm = i } else { rr.Algorithm = uint8(i) } - <-c // _BLANK + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad TA DigestType", l}, "" - } else { - rr.DigestType = uint8(i) } + rr.DigestType = uint8(i) s, e, c1 := endingToString(c, "bad TA Digest", f) if e != nil { - return nil, e, c1 + return nil, e.(*ParseError), c1 } rr.Digest = s return rr, nil, c1 @@ -1727,28 +1776,29 @@ func setTLSA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, l.comment } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad TLSA Usage", l}, "" - } else { - rr.Usage = uint8(i) } - <-c // _BLANK + rr.Usage = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad TLSA Selector", l}, "" - } else { - rr.Selector = uint8(i) } - <-c // _BLANK + rr.Selector = uint8(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { 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.MatchingType = uint8(i) + // So this needs be e2 (i.e. different than e), because...??t + s, e2, c1 := endingToString(c, "bad TLSA Certificate", f) + if e2 != nil { + return nil, e2, c1 } rr.Certificate = s return rr, nil, c1 @@ -1761,10 +1811,10 @@ func setRFC3597(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) if l.token != "\\#" { return nil, &ParseError{f, "bad RFC3597 Rdata", l}, "" } - <-c // _BLANK + <-c // zBlank l = <-c rdlength, e := strconv.Atoi(l.token) - if e != nil { + if e != nil || l.err { return nil, &ParseError{f, "bad RFC3597 Rdata ", l}, "" } @@ -1795,7 +1845,7 @@ func setTXT(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(TXT) rr.Hdr = h - // No _BLANK reading here, because this is all rdata is TXT + // no zBlank reading here, because all this rdata is TXT s, e, c1 := endingToTxtSlice(c, "bad TXT Txt", f) if e != nil { return nil, e, "" @@ -1822,66 +1872,32 @@ func setURI(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr.Hdr = h l := <-c - if l.length == 0 { - return rr, nil, l.comment + if l.length == 0 { // Dynamic updates. + return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad URI Priority", l}, "" - } else { - rr.Priority = uint16(i) } - <-c // _BLANK + rr.Priority = uint16(i) + <-c // zBlank l = <-c - if i, e := strconv.Atoi(l.token); e != nil { + i, e = strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad URI Weight", l}, "" - } else { - rr.Weight = uint16(i) } + rr.Weight = uint16(i) - <-c // _BLANK - s, e, c1 := endingToTxtSlice(c, "bad URI Target", f) - if e != nil { - return nil, e, "" + <-c // zBlank + s, err, c1 := endingToTxtSlice(c, "bad URI Target", f) + if err != nil { + return nil, err, "" } - 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 l.length == 0 { - return rr, nil, l.comment + if len(s) > 1 { + return nil, &ParseError{f, "bad URI Target", l}, "" } - 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 + rr.Target = s[0] return rr, nil, c1 } @@ -1906,15 +1922,15 @@ func setNID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad NID Preference", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString u, err := stringToNodeID(l) - if err != nil { + if err != nil || l.err { return nil, err, "" } rr.NodeID = u @@ -1929,15 +1945,15 @@ func setL32(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad L32 Preference", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString rr.Locator32 = net.ParseIP(l.token) - if rr.Locator32 == nil { + if rr.Locator32 == nil || l.err { return nil, &ParseError{f, "bad L32 Locator", l}, "" } return rr, nil, "" @@ -1951,13 +1967,13 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad LP Preference", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString rr.Fqdn = l.token if l.length == 0 { return rr, nil, "" @@ -1967,7 +1983,7 @@ func setLP(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad LP Fqdn", l}, "" } if rr.Fqdn[l.length-1] != '.' { @@ -1984,15 +2000,15 @@ func setL64(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad L64 Preference", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString u, err := stringToNodeID(l) - if err != nil { + if err != nil || l.err { return nil, err, "" } rr.Locator64 = u @@ -2006,11 +2022,11 @@ func setUID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad UID Uid", l}, "" - } else { - rr.Uid = uint32(i) } + rr.Uid = uint32(i) return rr, nil, "" } @@ -2021,11 +2037,11 @@ func setGID(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad GID Gid", l}, "" - } else { - rr.Gid = uint32(i) } + rr.Gid = uint32(i) return rr, nil, "" } @@ -2048,13 +2064,13 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { if l.length == 0 { return rr, nil, "" } - if i, e := strconv.Atoi(l.token); e != nil { + i, e := strconv.Atoi(l.token) + if e != nil || l.err { return nil, &ParseError{f, "bad PX Preference", l}, "" - } else { - rr.Preference = uint16(i) } - <-c // _BLANK - l = <-c // _STRING + rr.Preference = uint16(i) + <-c // zBlank + l = <-c // zString rr.Map822 = l.token if l.length == 0 { return rr, nil, "" @@ -2064,21 +2080,21 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } _, ok := IsDomainName(l.token) - if !ok { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad PX Map822", l}, "" } if rr.Map822[l.length-1] != '.' { rr.Map822 = appendOrigin(rr.Map822, o) } - <-c // _BLANK - l = <-c // _STRING + <-c // zBlank + l = <-c // zString rr.Mapx400 = l.token if l.token == "@" { rr.Mapx400 = o return rr, nil, "" } _, ok = IsDomainName(l.token) - if !ok || l.length == 0 { + if !ok || l.length == 0 || l.err { return nil, &ParseError{f, "bad PX Mapx400", l}, "" } if rr.Mapx400[l.length-1] != '.' { @@ -2087,69 +2103,168 @@ func setPX(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { return rr, nil, "" } -var typeToparserFunc = map[uint16]parserFunc{ - TypeAAAA: parserFunc{setAAAA, false}, - TypeAFSDB: parserFunc{setAFSDB, false}, - TypeA: parserFunc{setA, false}, - TypeCDS: parserFunc{setCDS, true}, - TypeCDNSKEY: parserFunc{setCDNSKEY, true}, - TypeCERT: parserFunc{setCERT, true}, - TypeCNAME: parserFunc{setCNAME, false}, - TypeDHCID: parserFunc{setDHCID, true}, - TypeDLV: parserFunc{setDLV, true}, - TypeDNAME: parserFunc{setDNAME, false}, - TypeKEY: parserFunc{setKEY, true}, - TypeDNSKEY: parserFunc{setDNSKEY, true}, - TypeDS: parserFunc{setDS, true}, - TypeEID: parserFunc{setEID, true}, - TypeEUI48: parserFunc{setEUI48, false}, - TypeEUI64: parserFunc{setEUI64, false}, - TypeGID: parserFunc{setGID, false}, - TypeGPOS: parserFunc{setGPOS, false}, - TypeHINFO: parserFunc{setHINFO, false}, - TypeHIP: parserFunc{setHIP, true}, - TypeIPSECKEY: parserFunc{setIPSECKEY, true}, - TypeKX: parserFunc{setKX, false}, - TypeL32: parserFunc{setL32, false}, - TypeL64: parserFunc{setL64, false}, - TypeLOC: parserFunc{setLOC, true}, - TypeLP: parserFunc{setLP, false}, - TypeMB: parserFunc{setMB, false}, - TypeMD: parserFunc{setMD, false}, - TypeMF: parserFunc{setMF, false}, - TypeMG: parserFunc{setMG, false}, - TypeMINFO: parserFunc{setMINFO, false}, - TypeMR: parserFunc{setMR, false}, - TypeMX: parserFunc{setMX, false}, - TypeNAPTR: parserFunc{setNAPTR, false}, - TypeNID: parserFunc{setNID, false}, - TypeNIMLOC: parserFunc{setNIMLOC, true}, - TypeNINFO: parserFunc{setNINFO, true}, - TypeNSAP: parserFunc{setNSAP, true}, - TypeNSAPPTR: parserFunc{setNSAPPTR, false}, - TypeNSEC3PARAM: parserFunc{setNSEC3PARAM, false}, - TypeNSEC3: parserFunc{setNSEC3, true}, - TypeNSEC: parserFunc{setNSEC, true}, - TypeNS: parserFunc{setNS, false}, - TypeOPENPGPKEY: parserFunc{setOPENPGPKEY, true}, - TypePTR: parserFunc{setPTR, false}, - TypePX: parserFunc{setPX, false}, - TypeSIG: parserFunc{setSIG, true}, - TypeRKEY: parserFunc{setRKEY, true}, - TypeRP: parserFunc{setRP, false}, - TypeRRSIG: parserFunc{setRRSIG, true}, - TypeRT: parserFunc{setRT, false}, - TypeSOA: parserFunc{setSOA, false}, - TypeSPF: parserFunc{setSPF, true}, - TypeSRV: parserFunc{setSRV, false}, - TypeSSHFP: parserFunc{setSSHFP, false}, - TypeTALINK: parserFunc{setTALINK, false}, - TypeTA: parserFunc{setTA, true}, - TypeTLSA: parserFunc{setTLSA, true}, - TypeTXT: parserFunc{setTXT, true}, - TypeUID: parserFunc{setUID, false}, - TypeUINFO: parserFunc{setUINFO, true}, - TypeURI: parserFunc{setURI, true}, - TypeWKS: parserFunc{setWKS, true}, - TypeX25: parserFunc{setX25, false}, +func setIPSECKEY(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { + rr := new(IPSECKEY) + rr.Hdr = h + l := <-c + if l.length == 0 { + return rr, nil, l.comment + } + i, err := strconv.Atoi(l.token) + if err != nil || l.err { + return nil, &ParseError{f, "bad IPSECKEY Precedence", l}, "" + } + rr.Precedence = uint8(i) + <-c // zBlank + l = <-c + i, err = strconv.Atoi(l.token) + if err != nil || l.err { + return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" + } + rr.GatewayType = uint8(i) + <-c // zBlank + l = <-c + i, err = strconv.Atoi(l.token) + if err != nil || l.err { + return nil, &ParseError{f, "bad IPSECKEY Algorithm", l}, "" + } + rr.Algorithm = uint8(i) + + // Now according to GatewayType we can have different elements here + <-c // zBlank + l = <-c + switch rr.GatewayType { + case 0: + fallthrough + case 3: + rr.GatewayName = l.token + if l.token == "@" { + rr.GatewayName = o + } + _, ok := IsDomainName(l.token) + if !ok || l.length == 0 || l.err { + return nil, &ParseError{f, "bad IPSECKEY GatewayName", l}, "" + } + if rr.GatewayName[l.length-1] != '.' { + rr.GatewayName = appendOrigin(rr.GatewayName, o) + } + case 1: + rr.GatewayA = net.ParseIP(l.token) + if rr.GatewayA == nil { + return nil, &ParseError{f, "bad IPSECKEY GatewayA", l}, "" + } + case 2: + rr.GatewayAAAA = net.ParseIP(l.token) + if rr.GatewayAAAA == nil { + return nil, &ParseError{f, "bad IPSECKEY GatewayAAAA", l}, "" + } + default: + return nil, &ParseError{f, "bad IPSECKEY GatewayType", l}, "" + } + + s, e, c1 := endingToString(c, "bad IPSECKEY PublicKey", f) + if e != nil { + return nil, e, c1 + } + rr.PublicKey = s + return rr, nil, c1 +} + +func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { + rr := new(CAA) + rr.Hdr = h + l := <-c + if l.length == 0 { + return rr, nil, l.comment + } + i, err := strconv.Atoi(l.token) + if err != nil || l.err { + return nil, &ParseError{f, "bad CAA Flag", l}, "" + } + rr.Flag = uint8(i) + + <-c // zBlank + l = <-c // zString + if l.value != zString { + return nil, &ParseError{f, "bad CAA Tag", l}, "" + } + rr.Tag = l.token + + <-c // zBlank + s, e, c1 := endingToTxtSlice(c, "bad CAA Value", f) + if e != nil { + return nil, e, "" + } + if len(s) > 1 { + return nil, &ParseError{f, "bad CAA Value", l}, "" + } + rr.Value = s[0] + return rr, nil, c1 +} + +var typeToparserFunc = map[uint16]parserFunc{ + TypeAAAA: {setAAAA, false}, + TypeAFSDB: {setAFSDB, false}, + TypeA: {setA, false}, + TypeCAA: {setCAA, true}, + TypeCDS: {setCDS, true}, + TypeCDNSKEY: {setCDNSKEY, true}, + TypeCERT: {setCERT, true}, + TypeCNAME: {setCNAME, false}, + TypeDHCID: {setDHCID, true}, + TypeDLV: {setDLV, true}, + TypeDNAME: {setDNAME, false}, + TypeKEY: {setKEY, true}, + TypeDNSKEY: {setDNSKEY, true}, + TypeDS: {setDS, true}, + TypeEID: {setEID, true}, + TypeEUI48: {setEUI48, false}, + TypeEUI64: {setEUI64, false}, + TypeGID: {setGID, false}, + TypeGPOS: {setGPOS, false}, + TypeHINFO: {setHINFO, true}, + TypeHIP: {setHIP, true}, + TypeIPSECKEY: {setIPSECKEY, true}, + TypeKX: {setKX, false}, + TypeL32: {setL32, false}, + TypeL64: {setL64, false}, + TypeLOC: {setLOC, true}, + TypeLP: {setLP, false}, + TypeMB: {setMB, false}, + TypeMD: {setMD, false}, + TypeMF: {setMF, false}, + TypeMG: {setMG, false}, + TypeMINFO: {setMINFO, false}, + TypeMR: {setMR, false}, + TypeMX: {setMX, false}, + TypeNAPTR: {setNAPTR, false}, + TypeNID: {setNID, false}, + TypeNIMLOC: {setNIMLOC, true}, + TypeNINFO: {setNINFO, true}, + TypeNSAPPTR: {setNSAPPTR, false}, + TypeNSEC3PARAM: {setNSEC3PARAM, false}, + TypeNSEC3: {setNSEC3, true}, + TypeNSEC: {setNSEC, true}, + TypeNS: {setNS, false}, + TypeOPENPGPKEY: {setOPENPGPKEY, true}, + TypePTR: {setPTR, false}, + TypePX: {setPX, false}, + TypeSIG: {setSIG, true}, + TypeRKEY: {setRKEY, true}, + TypeRP: {setRP, false}, + TypeRRSIG: {setRRSIG, true}, + TypeRT: {setRT, false}, + TypeSOA: {setSOA, false}, + TypeSPF: {setSPF, true}, + TypeSRV: {setSRV, false}, + TypeSSHFP: {setSSHFP, true}, + TypeTALINK: {setTALINK, false}, + TypeTA: {setTA, true}, + TypeTLSA: {setTLSA, true}, + TypeTXT: {setTXT, true}, + TypeUID: {setUID, false}, + TypeUINFO: {setUINFO, true}, + TypeURI: {setURI, true}, + TypeWKS: {setWKS, true}, + TypeX25: {setX25, false}, } diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go new file mode 100644 index 00000000000..3d0f9aef573 --- /dev/null +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -0,0 +1,842 @@ +// *** DO NOT MODIFY *** +// AUTOGENERATED BY go generate + +package dns + +import ( + "encoding/base64" + "net" +) + +// TypeToRR is a map of constructors for each RR type. +var TypeToRR = map[uint16]func() RR{ + TypeA: func() RR { return new(A) }, + TypeAAAA: func() RR { return new(AAAA) }, + TypeAFSDB: func() RR { return new(AFSDB) }, + TypeANY: func() RR { return new(ANY) }, + TypeCAA: func() RR { return new(CAA) }, + TypeCDNSKEY: func() RR { return new(CDNSKEY) }, + TypeCDS: func() RR { return new(CDS) }, + TypeCERT: func() RR { return new(CERT) }, + TypeCNAME: func() RR { return new(CNAME) }, + TypeDHCID: func() RR { return new(DHCID) }, + TypeDLV: func() RR { return new(DLV) }, + TypeDNAME: func() RR { return new(DNAME) }, + TypeDNSKEY: func() RR { return new(DNSKEY) }, + TypeDS: func() RR { return new(DS) }, + TypeEID: func() RR { return new(EID) }, + TypeEUI48: func() RR { return new(EUI48) }, + TypeEUI64: func() RR { return new(EUI64) }, + TypeGID: func() RR { return new(GID) }, + TypeGPOS: func() RR { return new(GPOS) }, + TypeHINFO: func() RR { return new(HINFO) }, + TypeHIP: func() RR { return new(HIP) }, + TypeIPSECKEY: func() RR { return new(IPSECKEY) }, + TypeKEY: func() RR { return new(KEY) }, + TypeKX: func() RR { return new(KX) }, + TypeL32: func() RR { return new(L32) }, + TypeL64: func() RR { return new(L64) }, + TypeLOC: func() RR { return new(LOC) }, + TypeLP: func() RR { return new(LP) }, + TypeMB: func() RR { return new(MB) }, + TypeMD: func() RR { return new(MD) }, + TypeMF: func() RR { return new(MF) }, + TypeMG: func() RR { return new(MG) }, + TypeMINFO: func() RR { return new(MINFO) }, + TypeMR: func() RR { return new(MR) }, + TypeMX: func() RR { return new(MX) }, + TypeNAPTR: func() RR { return new(NAPTR) }, + TypeNID: func() RR { return new(NID) }, + TypeNIMLOC: func() RR { return new(NIMLOC) }, + TypeNINFO: func() RR { return new(NINFO) }, + TypeNS: func() RR { return new(NS) }, + TypeNSAPPTR: func() RR { return new(NSAPPTR) }, + TypeNSEC: func() RR { return new(NSEC) }, + TypeNSEC3: func() RR { return new(NSEC3) }, + TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, + TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) }, + TypeOPT: func() RR { return new(OPT) }, + TypePTR: func() RR { return new(PTR) }, + TypePX: func() RR { return new(PX) }, + TypeRKEY: func() RR { return new(RKEY) }, + TypeRP: func() RR { return new(RP) }, + TypeRRSIG: func() RR { return new(RRSIG) }, + TypeRT: func() RR { return new(RT) }, + TypeSIG: func() RR { return new(SIG) }, + TypeSOA: func() RR { return new(SOA) }, + TypeSPF: func() RR { return new(SPF) }, + TypeSRV: func() RR { return new(SRV) }, + TypeSSHFP: func() RR { return new(SSHFP) }, + TypeTA: func() RR { return new(TA) }, + TypeTALINK: func() RR { return new(TALINK) }, + TypeTKEY: func() RR { return new(TKEY) }, + TypeTLSA: func() RR { return new(TLSA) }, + TypeTSIG: func() RR { return new(TSIG) }, + TypeTXT: func() RR { return new(TXT) }, + TypeUID: func() RR { return new(UID) }, + TypeUINFO: func() RR { return new(UINFO) }, + TypeURI: func() RR { return new(URI) }, + TypeWKS: func() RR { return new(WKS) }, + TypeX25: func() RR { return new(X25) }, +} + +// TypeToString is a map of strings for each RR type. +var TypeToString = map[uint16]string{ + TypeA: "A", + TypeAAAA: "AAAA", + TypeAFSDB: "AFSDB", + TypeANY: "ANY", + TypeATMA: "ATMA", + TypeAXFR: "AXFR", + TypeCAA: "CAA", + TypeCDNSKEY: "CDNSKEY", + TypeCDS: "CDS", + TypeCERT: "CERT", + TypeCNAME: "CNAME", + TypeDHCID: "DHCID", + TypeDLV: "DLV", + TypeDNAME: "DNAME", + TypeDNSKEY: "DNSKEY", + TypeDS: "DS", + TypeEID: "EID", + TypeEUI48: "EUI48", + TypeEUI64: "EUI64", + TypeGID: "GID", + TypeGPOS: "GPOS", + TypeHINFO: "HINFO", + TypeHIP: "HIP", + TypeIPSECKEY: "IPSECKEY", + TypeISDN: "ISDN", + TypeIXFR: "IXFR", + TypeKEY: "KEY", + TypeKX: "KX", + TypeL32: "L32", + TypeL64: "L64", + TypeLOC: "LOC", + TypeLP: "LP", + TypeMAILA: "MAILA", + TypeMAILB: "MAILB", + TypeMB: "MB", + TypeMD: "MD", + TypeMF: "MF", + TypeMG: "MG", + TypeMINFO: "MINFO", + TypeMR: "MR", + TypeMX: "MX", + TypeNAPTR: "NAPTR", + TypeNID: "NID", + TypeNIMLOC: "NIMLOC", + TypeNINFO: "NINFO", + TypeNS: "NS", + TypeNSEC: "NSEC", + TypeNSEC3: "NSEC3", + TypeNSEC3PARAM: "NSEC3PARAM", + TypeNULL: "NULL", + TypeNXT: "NXT", + TypeNone: "None", + TypeOPENPGPKEY: "OPENPGPKEY", + TypeOPT: "OPT", + TypePTR: "PTR", + TypePX: "PX", + TypeRKEY: "RKEY", + TypeRP: "RP", + TypeRRSIG: "RRSIG", + TypeRT: "RT", + TypeReserved: "Reserved", + TypeSIG: "SIG", + TypeSOA: "SOA", + TypeSPF: "SPF", + TypeSRV: "SRV", + TypeSSHFP: "SSHFP", + TypeTA: "TA", + TypeTALINK: "TALINK", + TypeTKEY: "TKEY", + TypeTLSA: "TLSA", + TypeTSIG: "TSIG", + TypeTXT: "TXT", + TypeUID: "UID", + TypeUINFO: "UINFO", + TypeUNSPEC: "UNSPEC", + TypeURI: "URI", + TypeWKS: "WKS", + TypeX25: "X25", + TypeNSAPPTR: "NSAP-PTR", +} + +// Header() functions +func (rr *A) Header() *RR_Header { return &rr.Hdr } +func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } +func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } +func (rr *ANY) Header() *RR_Header { return &rr.Hdr } +func (rr *CAA) Header() *RR_Header { return &rr.Hdr } +func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *CDS) Header() *RR_Header { return &rr.Hdr } +func (rr *CERT) Header() *RR_Header { return &rr.Hdr } +func (rr *CNAME) Header() *RR_Header { return &rr.Hdr } +func (rr *DHCID) Header() *RR_Header { return &rr.Hdr } +func (rr *DLV) Header() *RR_Header { return &rr.Hdr } +func (rr *DNAME) Header() *RR_Header { return &rr.Hdr } +func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *DS) Header() *RR_Header { return &rr.Hdr } +func (rr *EID) Header() *RR_Header { return &rr.Hdr } +func (rr *EUI48) Header() *RR_Header { return &rr.Hdr } +func (rr *EUI64) Header() *RR_Header { return &rr.Hdr } +func (rr *GID) Header() *RR_Header { return &rr.Hdr } +func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } +func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } +func (rr *HIP) Header() *RR_Header { return &rr.Hdr } +func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *KEY) Header() *RR_Header { return &rr.Hdr } +func (rr *KX) Header() *RR_Header { return &rr.Hdr } +func (rr *L32) Header() *RR_Header { return &rr.Hdr } +func (rr *L64) Header() *RR_Header { return &rr.Hdr } +func (rr *LOC) Header() *RR_Header { return &rr.Hdr } +func (rr *LP) Header() *RR_Header { return &rr.Hdr } +func (rr *MB) Header() *RR_Header { return &rr.Hdr } +func (rr *MD) Header() *RR_Header { return &rr.Hdr } +func (rr *MF) Header() *RR_Header { return &rr.Hdr } +func (rr *MG) Header() *RR_Header { return &rr.Hdr } +func (rr *MINFO) Header() *RR_Header { return &rr.Hdr } +func (rr *MR) Header() *RR_Header { return &rr.Hdr } +func (rr *MX) Header() *RR_Header { return &rr.Hdr } +func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr } +func (rr *NID) Header() *RR_Header { return &rr.Hdr } +func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr } +func (rr *NINFO) Header() *RR_Header { return &rr.Hdr } +func (rr *NS) Header() *RR_Header { return &rr.Hdr } +func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr } +func (rr *NSEC) Header() *RR_Header { return &rr.Hdr } +func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr } +func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr } +func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *OPT) Header() *RR_Header { return &rr.Hdr } +func (rr *PTR) Header() *RR_Header { return &rr.Hdr } +func (rr *PX) Header() *RR_Header { return &rr.Hdr } +func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr } +func (rr *RKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *RP) Header() *RR_Header { return &rr.Hdr } +func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr } +func (rr *RT) Header() *RR_Header { return &rr.Hdr } +func (rr *SIG) Header() *RR_Header { return &rr.Hdr } +func (rr *SOA) Header() *RR_Header { return &rr.Hdr } +func (rr *SPF) Header() *RR_Header { return &rr.Hdr } +func (rr *SRV) Header() *RR_Header { return &rr.Hdr } +func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr } +func (rr *TA) Header() *RR_Header { return &rr.Hdr } +func (rr *TALINK) Header() *RR_Header { return &rr.Hdr } +func (rr *TKEY) Header() *RR_Header { return &rr.Hdr } +func (rr *TLSA) Header() *RR_Header { return &rr.Hdr } +func (rr *TSIG) Header() *RR_Header { return &rr.Hdr } +func (rr *TXT) Header() *RR_Header { return &rr.Hdr } +func (rr *UID) Header() *RR_Header { return &rr.Hdr } +func (rr *UINFO) Header() *RR_Header { return &rr.Hdr } +func (rr *URI) Header() *RR_Header { return &rr.Hdr } +func (rr *WKS) Header() *RR_Header { return &rr.Hdr } +func (rr *X25) Header() *RR_Header { return &rr.Hdr } + +// len() functions +func (rr *A) len() int { + l := rr.Hdr.len() + l += net.IPv4len // A + return l +} +func (rr *AAAA) len() int { + l := rr.Hdr.len() + l += net.IPv6len // AAAA + return l +} +func (rr *AFSDB) len() int { + l := rr.Hdr.len() + l += 2 // Subtype + l += len(rr.Hostname) + 1 + return l +} +func (rr *ANY) len() int { + l := rr.Hdr.len() + return l +} +func (rr *CAA) len() int { + l := rr.Hdr.len() + l += 1 // Flag + l += len(rr.Tag) + 1 + l += len(rr.Value) + return l +} +func (rr *CERT) len() int { + l := rr.Hdr.len() + l += 2 // Type + l += 2 // KeyTag + l += 1 // Algorithm + l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) + return l +} +func (rr *CNAME) len() int { + l := rr.Hdr.len() + l += len(rr.Target) + 1 + return l +} +func (rr *DHCID) len() int { + l := rr.Hdr.len() + l += base64.StdEncoding.DecodedLen(len(rr.Digest)) + return l +} +func (rr *DNAME) len() int { + l := rr.Hdr.len() + l += len(rr.Target) + 1 + return l +} +func (rr *DNSKEY) len() int { + l := rr.Hdr.len() + l += 2 // Flags + l += 1 // Protocol + l += 1 // Algorithm + l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + return l +} +func (rr *DS) len() int { + l := rr.Hdr.len() + l += 2 // KeyTag + l += 1 // Algorithm + l += 1 // DigestType + l += len(rr.Digest)/2 + 1 + return l +} +func (rr *EID) len() int { + l := rr.Hdr.len() + l += len(rr.Endpoint)/2 + 1 + return l +} +func (rr *EUI48) len() int { + l := rr.Hdr.len() + l += 6 // Address + return l +} +func (rr *EUI64) len() int { + l := rr.Hdr.len() + l += 8 // Address + return l +} +func (rr *GID) len() int { + l := rr.Hdr.len() + l += 4 // Gid + return l +} +func (rr *GPOS) len() int { + l := rr.Hdr.len() + l += len(rr.Longitude) + 1 + l += len(rr.Latitude) + 1 + l += len(rr.Altitude) + 1 + return l +} +func (rr *HINFO) len() int { + l := rr.Hdr.len() + l += len(rr.Cpu) + 1 + l += len(rr.Os) + 1 + return l +} +func (rr *HIP) len() int { + l := rr.Hdr.len() + l += 1 // HitLength + l += 1 // PublicKeyAlgorithm + l += 2 // PublicKeyLength + l += len(rr.Hit)/2 + 1 + l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + for _, x := range rr.RendezvousServers { + l += len(x) + 1 + } + return l +} +func (rr *KX) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += len(rr.Exchanger) + 1 + return l +} +func (rr *L32) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += net.IPv4len // Locator32 + return l +} +func (rr *L64) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += 8 // Locator64 + return l +} +func (rr *LOC) len() int { + l := rr.Hdr.len() + l += 1 // Version + l += 1 // Size + l += 1 // HorizPre + l += 1 // VertPre + l += 4 // Latitude + l += 4 // Longitude + l += 4 // Altitude + return l +} +func (rr *LP) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += len(rr.Fqdn) + 1 + return l +} +func (rr *MB) len() int { + l := rr.Hdr.len() + l += len(rr.Mb) + 1 + return l +} +func (rr *MD) len() int { + l := rr.Hdr.len() + l += len(rr.Md) + 1 + return l +} +func (rr *MF) len() int { + l := rr.Hdr.len() + l += len(rr.Mf) + 1 + return l +} +func (rr *MG) len() int { + l := rr.Hdr.len() + l += len(rr.Mg) + 1 + return l +} +func (rr *MINFO) len() int { + l := rr.Hdr.len() + l += len(rr.Rmail) + 1 + l += len(rr.Email) + 1 + return l +} +func (rr *MR) len() int { + l := rr.Hdr.len() + l += len(rr.Mr) + 1 + return l +} +func (rr *MX) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += len(rr.Mx) + 1 + return l +} +func (rr *NAPTR) len() int { + l := rr.Hdr.len() + l += 2 // Order + l += 2 // Preference + l += len(rr.Flags) + 1 + l += len(rr.Service) + 1 + l += len(rr.Regexp) + 1 + l += len(rr.Replacement) + 1 + return l +} +func (rr *NID) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += 8 // NodeID + return l +} +func (rr *NIMLOC) len() int { + l := rr.Hdr.len() + l += len(rr.Locator)/2 + 1 + return l +} +func (rr *NINFO) len() int { + l := rr.Hdr.len() + for _, x := range rr.ZSData { + l += len(x) + 1 + } + return l +} +func (rr *NS) len() int { + l := rr.Hdr.len() + l += len(rr.Ns) + 1 + return l +} +func (rr *NSAPPTR) len() int { + l := rr.Hdr.len() + l += len(rr.Ptr) + 1 + return l +} +func (rr *NSEC3PARAM) len() int { + l := rr.Hdr.len() + l += 1 // Hash + l += 1 // Flags + l += 2 // Iterations + l += 1 // SaltLength + l += len(rr.Salt)/2 + 1 + return l +} +func (rr *OPENPGPKEY) len() int { + l := rr.Hdr.len() + l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + return l +} +func (rr *PTR) len() int { + l := rr.Hdr.len() + l += len(rr.Ptr) + 1 + return l +} +func (rr *PX) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += len(rr.Map822) + 1 + l += len(rr.Mapx400) + 1 + return l +} +func (rr *RFC3597) len() int { + l := rr.Hdr.len() + l += len(rr.Rdata)/2 + 1 + return l +} +func (rr *RKEY) len() int { + l := rr.Hdr.len() + l += 2 // Flags + l += 1 // Protocol + l += 1 // Algorithm + l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) + return l +} +func (rr *RP) len() int { + l := rr.Hdr.len() + l += len(rr.Mbox) + 1 + l += len(rr.Txt) + 1 + return l +} +func (rr *RRSIG) len() int { + l := rr.Hdr.len() + l += 2 // TypeCovered + l += 1 // Algorithm + l += 1 // Labels + l += 4 // OrigTtl + l += 4 // Expiration + l += 4 // Inception + l += 2 // KeyTag + l += len(rr.SignerName) + 1 + l += base64.StdEncoding.DecodedLen(len(rr.Signature)) + return l +} +func (rr *RT) len() int { + l := rr.Hdr.len() + l += 2 // Preference + l += len(rr.Host) + 1 + return l +} +func (rr *SOA) len() int { + l := rr.Hdr.len() + l += len(rr.Ns) + 1 + l += len(rr.Mbox) + 1 + l += 4 // Serial + l += 4 // Refresh + l += 4 // Retry + l += 4 // Expire + l += 4 // Minttl + return l +} +func (rr *SPF) len() int { + l := rr.Hdr.len() + for _, x := range rr.Txt { + l += len(x) + 1 + } + return l +} +func (rr *SRV) len() int { + l := rr.Hdr.len() + l += 2 // Priority + l += 2 // Weight + l += 2 // Port + l += len(rr.Target) + 1 + return l +} +func (rr *SSHFP) len() int { + l := rr.Hdr.len() + l += 1 // Algorithm + l += 1 // Type + l += len(rr.FingerPrint)/2 + 1 + return l +} +func (rr *TA) len() int { + l := rr.Hdr.len() + l += 2 // KeyTag + l += 1 // Algorithm + l += 1 // DigestType + l += len(rr.Digest)/2 + 1 + return l +} +func (rr *TALINK) len() int { + l := rr.Hdr.len() + l += len(rr.PreviousName) + 1 + l += len(rr.NextName) + 1 + return l +} +func (rr *TKEY) len() int { + l := rr.Hdr.len() + l += len(rr.Algorithm) + 1 + l += 4 // Inception + l += 4 // Expiration + l += 2 // Mode + l += 2 // Error + l += 2 // KeySize + l += len(rr.Key) + 1 + l += 2 // OtherLen + l += len(rr.OtherData) + 1 + return l +} +func (rr *TLSA) len() int { + l := rr.Hdr.len() + l += 1 // Usage + l += 1 // Selector + l += 1 // MatchingType + l += len(rr.Certificate)/2 + 1 + return l +} +func (rr *TSIG) len() int { + l := rr.Hdr.len() + l += len(rr.Algorithm) + 1 + l += 6 // TimeSigned + l += 2 // Fudge + l += 2 // MACSize + l += len(rr.MAC)/2 + 1 + l += 2 // OrigId + l += 2 // Error + l += 2 // OtherLen + l += len(rr.OtherData)/2 + 1 + return l +} +func (rr *TXT) len() int { + l := rr.Hdr.len() + for _, x := range rr.Txt { + l += len(x) + 1 + } + return l +} +func (rr *UID) len() int { + l := rr.Hdr.len() + l += 4 // Uid + return l +} +func (rr *UINFO) len() int { + l := rr.Hdr.len() + l += len(rr.Uinfo) + 1 + return l +} +func (rr *URI) len() int { + l := rr.Hdr.len() + l += 2 // Priority + l += 2 // Weight + l += len(rr.Target) + return l +} +func (rr *X25) len() int { + l := rr.Hdr.len() + l += len(rr.PSDNAddress) + 1 + return l +} + +// copy() functions +func (rr *A) copy() RR { + return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)} +} +func (rr *AAAA) copy() RR { + return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)} +} +func (rr *AFSDB) copy() RR { + return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname} +} +func (rr *ANY) copy() RR { + return &ANY{*rr.Hdr.copyHeader()} +} +func (rr *CAA) copy() RR { + return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} +} +func (rr *CERT) copy() RR { + return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} +} +func (rr *CNAME) copy() RR { + return &CNAME{*rr.Hdr.copyHeader(), rr.Target} +} +func (rr *DHCID) copy() RR { + return &DHCID{*rr.Hdr.copyHeader(), rr.Digest} +} +func (rr *DNAME) copy() RR { + return &DNAME{*rr.Hdr.copyHeader(), rr.Target} +} +func (rr *DNSKEY) copy() RR { + return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} +} +func (rr *DS) copy() RR { + return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} +} +func (rr *EID) copy() RR { + return &EID{*rr.Hdr.copyHeader(), rr.Endpoint} +} +func (rr *EUI48) copy() RR { + return &EUI48{*rr.Hdr.copyHeader(), rr.Address} +} +func (rr *EUI64) copy() RR { + return &EUI64{*rr.Hdr.copyHeader(), rr.Address} +} +func (rr *GID) copy() RR { + return &GID{*rr.Hdr.copyHeader(), rr.Gid} +} +func (rr *GPOS) copy() RR { + return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude} +} +func (rr *HINFO) copy() RR { + return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os} +} +func (rr *HIP) copy() RR { + RendezvousServers := make([]string, len(rr.RendezvousServers)) + copy(RendezvousServers, rr.RendezvousServers) + return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers} +} +func (rr *IPSECKEY) copy() RR { + return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, copyIP(rr.GatewayA), copyIP(rr.GatewayAAAA), rr.GatewayName, rr.PublicKey} +} +func (rr *KX) copy() RR { + return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger} +} +func (rr *L32) copy() RR { + return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)} +} +func (rr *L64) copy() RR { + return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64} +} +func (rr *LOC) copy() RR { + return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude} +} +func (rr *LP) copy() RR { + return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn} +} +func (rr *MB) copy() RR { + return &MB{*rr.Hdr.copyHeader(), rr.Mb} +} +func (rr *MD) copy() RR { + return &MD{*rr.Hdr.copyHeader(), rr.Md} +} +func (rr *MF) copy() RR { + return &MF{*rr.Hdr.copyHeader(), rr.Mf} +} +func (rr *MG) copy() RR { + return &MG{*rr.Hdr.copyHeader(), rr.Mg} +} +func (rr *MINFO) copy() RR { + return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email} +} +func (rr *MR) copy() RR { + return &MR{*rr.Hdr.copyHeader(), rr.Mr} +} +func (rr *MX) copy() RR { + return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx} +} +func (rr *NAPTR) copy() RR { + return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement} +} +func (rr *NID) copy() RR { + return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID} +} +func (rr *NIMLOC) copy() RR { + return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator} +} +func (rr *NINFO) copy() RR { + ZSData := make([]string, len(rr.ZSData)) + copy(ZSData, rr.ZSData) + return &NINFO{*rr.Hdr.copyHeader(), ZSData} +} +func (rr *NS) copy() RR { + return &NS{*rr.Hdr.copyHeader(), rr.Ns} +} +func (rr *NSAPPTR) copy() RR { + return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr} +} +func (rr *NSEC) copy() RR { + TypeBitMap := make([]uint16, len(rr.TypeBitMap)) + copy(TypeBitMap, rr.TypeBitMap) + return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, TypeBitMap} +} +func (rr *NSEC3) copy() RR { + TypeBitMap := make([]uint16, len(rr.TypeBitMap)) + copy(TypeBitMap, rr.TypeBitMap) + return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap} +} +func (rr *NSEC3PARAM) copy() RR { + return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt} +} +func (rr *OPENPGPKEY) copy() RR { + return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey} +} +func (rr *OPT) copy() RR { + Option := make([]EDNS0, len(rr.Option)) + copy(Option, rr.Option) + return &OPT{*rr.Hdr.copyHeader(), Option} +} +func (rr *PTR) copy() RR { + return &PTR{*rr.Hdr.copyHeader(), rr.Ptr} +} +func (rr *PX) copy() RR { + return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400} +} +func (rr *RFC3597) copy() RR { + return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata} +} +func (rr *RKEY) copy() RR { + return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} +} +func (rr *RP) copy() RR { + return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt} +} +func (rr *RRSIG) copy() RR { + return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature} +} +func (rr *RT) copy() RR { + return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host} +} +func (rr *SOA) copy() RR { + return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl} +} +func (rr *SPF) copy() RR { + Txt := make([]string, len(rr.Txt)) + copy(Txt, rr.Txt) + return &SPF{*rr.Hdr.copyHeader(), Txt} +} +func (rr *SRV) copy() RR { + return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target} +} +func (rr *SSHFP) copy() RR { + return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint} +} +func (rr *TA) copy() RR { + return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} +} +func (rr *TALINK) copy() RR { + return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName} +} +func (rr *TKEY) copy() RR { + return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData} +} +func (rr *TLSA) copy() RR { + return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} +} +func (rr *TSIG) copy() RR { + return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData} +} +func (rr *TXT) copy() RR { + Txt := make([]string, len(rr.Txt)) + copy(Txt, rr.Txt) + return &TXT{*rr.Hdr.copyHeader(), Txt} +} +func (rr *UID) copy() RR { + return &UID{*rr.Hdr.copyHeader(), rr.Uid} +} +func (rr *UINFO) copy() RR { + return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo} +} +func (rr *URI) copy() RR { + return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target} +} +func (rr *WKS) copy() RR { + BitMap := make([]uint16, len(rr.BitMap)) + copy(BitMap, rr.BitMap) + return &WKS{*rr.Hdr.copyHeader(), copyIP(rr.Address), rr.Protocol, BitMap} +} +func (rr *X25) copy() RR { + return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress} +} diff --git a/vendor/github.com/skynetservices/skydns/msg/service.go b/vendor/github.com/skynetservices/skydns/msg/service.go index f0632e98471..e2d4a603bbe 100644 --- a/vendor/github.com/skynetservices/skydns/msg/service.go +++ b/vendor/github.com/skynetservices/skydns/msg/service.go @@ -12,6 +12,14 @@ import ( "github.com/miekg/dns" ) +// PathPrefix is the prefix used to store SkyDNS data in the backend. +// It defaults to `skydns`. +// You can change it by set `path-prefix` configuration or SKYDNS_PATH_PREFIX env. variable. +// Then: +// The SkyDNS's configuration object should be stored under the key "/mydns/config"; +// The etcd path of domain `service.staging.skydns.local.` will be "/mydns/local/skydns/staging/service". +var PathPrefix string = "skydns" + // This *is* the rdata from a SRV record, but with a twist. // Host (Target in SRV) must be a domain name, but if it looks like an IP // address (4/6), we will treat it like an IP address. @@ -29,7 +37,7 @@ type Service struct { // the record lives to a DNS name and use this as the srv.Target. When // TargetStrip > 0 we strip the left most TargetStrip labels from the // DNS name. - TargetStrip int `json:"targetstrip",omitempty"` + TargetStrip int `json:"targetstrip,omitempty"` // Group is used to group (or *not* to group) different services // together. Services with an identical Group are returned in the same @@ -42,17 +50,7 @@ type Service struct { // NewSRV returns a new SRV record based on the Service. func (s *Service) NewSRV(name string, weight uint16) *dns.SRV { - host := dns.Fqdn(s.Host) - - offset, end := 0, false - for i := 0; i < s.TargetStrip; i++ { - offset, end = dns.NextLabel(host, offset) - } - if end { - // We overshot the name, use the orignal one. - offset = 0 - } - host = host[offset:] + host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl}, Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: host} @@ -60,17 +58,7 @@ func (s *Service) NewSRV(name string, weight uint16) *dns.SRV { // NewMX returns a new MX record based on the Service. func (s *Service) NewMX(name string) *dns.MX { - host := dns.Fqdn(s.Host) - - offset, end := 0, false - for i := 0; i < s.TargetStrip; i++ { - offset, end = dns.NextLabel(host, offset) - } - if end { - // We overshot the name, use the orignal one. - offset = 0 - } - host = host[offset:] + host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.Ttl}, Preference: uint16(s.Priority), Mx: host} @@ -118,10 +106,10 @@ func PathWithWildcard(s string) (string, bool) { } for i, k := range l { if k == "*" || k == "any" { - return path.Join(append([]string{"/skydns/"}, l[:i]...)...), true + return path.Join(append([]string{"/" + PathPrefix + "/"}, l[:i]...)...), true } } - return path.Join(append([]string{"/skydns/"}, l...)...), false + return path.Join(append([]string{"/" + PathPrefix + "/"}, l...)...), false } // Path converts a domainname to an etcd path. If s looks like service.staging.skydns.local., @@ -131,7 +119,7 @@ func Path(s string) string { for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 { l[i], l[j] = l[j], l[i] } - return path.Join(append([]string{"/skydns/"}, l...)...) + return path.Join(append([]string{"/" + PathPrefix + "/"}, l...)...) } // Domain is the opposite of Path. @@ -213,3 +201,21 @@ func split255(s string) []string { return sx } + +// targetStrip strips "targetstrip" labels from the left side of the fully qualified name. +func targetStrip(name string, targetStrip int) string { + if targetStrip == 0 { + return name + } + + offset, end := 0, false + for i := 0; i < targetStrip; i++ { + offset, end = dns.NextLabel(name, offset) + } + if end { + // We overshot the name, use the orignal one. + offset = 0 + } + name = name[offset:] + return name +} From 9a6c7ed3a11c9d30aac6d4f7da558d4d2235b79e Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Wed, 4 May 2016 16:24:21 -0700 Subject: [PATCH 2/6] added pkg/dns with unit tests. --- pkg/dns/dns.go | 417 +++++++++++++++++++++++++++++++++++++++++++ pkg/dns/dns_test.go | 377 ++++++++++++++++++++++++++++++++++++++ pkg/dns/treecache.go | 312 ++++++++++++++++++++++++++++++++ 3 files changed, 1106 insertions(+) create mode 100644 pkg/dns/dns.go create mode 100644 pkg/dns/dns_test.go create mode 100644 pkg/dns/treecache.go diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go new file mode 100644 index 00000000000..6b184ce26c8 --- /dev/null +++ b/pkg/dns/dns.go @@ -0,0 +1,417 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dns + +import ( + "encoding/json" + "fmt" + "github.com/golang/glog" + "hash/fnv" + "net" + "strings" + "time" + + etcd "github.com/coreos/etcd/client" + skymsg "github.com/skynetservices/skydns/msg" + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/endpoints" + kcache "k8s.io/kubernetes/pkg/client/cache" + kclient "k8s.io/kubernetes/pkg/client/unversioned" + kframework "k8s.io/kubernetes/pkg/controller/framework" + kselector "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/util/validation" + "k8s.io/kubernetes/pkg/util/wait" +) + +const ( + kubernetesSvcName = "kubernetes" + + // A subdomain added to the user specified domain for all services. + serviceSubdomain = "svc" + + // A subdomain added to the user specified dmoain for all pods. + podSubdomain = "pod" + + // Resync period for the kube controller loop. + resyncPeriod = 30 * time.Minute +) + +type KubeDNS struct { + kubeClient *kclient.Client + // DNS domain name. + domain string + // A cache that contains all the endpoints in the system. + endpointsStore kcache.Store + // A cache that contains all the services in the system. + servicesStore kcache.Store + cache *TreeCache + domainPath []string + eController *kframework.Controller + serviceController *kframework.Controller +} + +func NewKubeDNS(client *kclient.Client, domain string) *KubeDNS { + kd := &KubeDNS{ + kubeClient: client, + domain: domain, + cache: NewTreeCache(), + domainPath: reverseArray(strings.Split(strings.TrimRight(domain, "."), ".")), + } + kd.setEndpointsStore() + kd.setServicesStore() + return kd +} + +func (kd *KubeDNS) Start() { + go kd.eController.Run(wait.NeverStop) + go kd.serviceController.Run(wait.NeverStop) + // Wait synchronously for the Kubernetes service and add a DNS record for it. + // TODO (abshah) UNCOMMENT AFTER TEST COMPLETE + //kd.waitForKubernetesService() +} + +func (kd *KubeDNS) waitForKubernetesService() (svc *kapi.Service) { + name := fmt.Sprintf("%v/%v", kapi.NamespaceDefault, kubernetesSvcName) + glog.Infof("Waiting for service: %v", name) + var err error + servicePollInterval := 1 * time.Second + for { + svc, err = kd.kubeClient.Services(kapi.NamespaceDefault).Get(kubernetesSvcName) + if err != nil || svc == nil { + glog.Infof("Ignoring error while waiting for service %v: %v. Sleeping %v before retrying.", name, err, servicePollInterval) + time.Sleep(servicePollInterval) + continue + } + break + } + return +} + +func (kd *KubeDNS) GetCacheAsJSON() string { + json, _ := kd.cache.Serialize("") + return json +} + +func (kd *KubeDNS) setServicesStore() { + // Returns a cache.ListWatch that gets all changes to services. + serviceWatch := kcache.NewListWatchFromClient(kd.kubeClient, "services", kapi.NamespaceAll, kselector.Everything()) + kd.servicesStore, kd.serviceController = kframework.NewInformer( + serviceWatch, + &kapi.Service{}, + resyncPeriod, + kframework.ResourceEventHandlerFuncs{ + AddFunc: kd.newService, + DeleteFunc: kd.removeService, + UpdateFunc: kd.updateService, + }, + ) +} + +func (kd *KubeDNS) setEndpointsStore() { + // Returns a cache.ListWatch that gets all changes to endpoints. + endpointsWatch := kcache.NewListWatchFromClient(kd.kubeClient, "endpoints", kapi.NamespaceAll, kselector.Everything()) + kd.endpointsStore, kd.eController = kframework.NewInformer( + endpointsWatch, + &kapi.Endpoints{}, + resyncPeriod, + kframework.ResourceEventHandlerFuncs{ + AddFunc: kd.handleEndpointAdd, + UpdateFunc: func(oldObj, newObj interface{}) { + // TODO: Avoid unwanted updates. + kd.handleEndpointAdd(newObj) + }, + }, + ) +} + +func (kd *KubeDNS) newService(obj interface{}) { + if service, ok := obj.(*kapi.Service); ok { + // if ClusterIP is not set, a DNS entry should not be created + if !kapi.IsServiceIPSet(service) { + kd.newHeadlessService(service) + return + } + if len(service.Spec.Ports) == 0 { + glog.Info("Unexpected service with no ports, this should not have happend: %v", service) + } + kd.newPortalService(service) + } +} + +func (kd *KubeDNS) removeService(obj interface{}) { + if s, ok := obj.(*kapi.Service); ok { + subCachePath := append(kd.domainPath, serviceSubdomain, s.Namespace, s.Name) + kd.cache.DeletePath(subCachePath...) + } +} + +func (kd *KubeDNS) updateService(oldObj, newObj interface{}) { + kd.newService(newObj) +} + +func (kd *KubeDNS) handleEndpointAdd(obj interface{}) { + if e, ok := obj.(*kapi.Endpoints); ok { + kd.addDNSUsingEndpoints(e) + } +} + +func (kd *KubeDNS) addDNSUsingEndpoints(e *kapi.Endpoints) error { + svc, err := kd.getServiceFromEndpoints(e) + if err != nil { + return err + } + if svc == nil || kapi.IsServiceIPSet(svc) { + // No headless service found corresponding to endpoints object. + return nil + } + return kd.generateRecordsForHeadlessService(e, svc) +} + +func (kd *KubeDNS) getServiceFromEndpoints(e *kapi.Endpoints) (*kapi.Service, error) { + key, err := kcache.MetaNamespaceKeyFunc(e) + if err != nil { + return nil, err + } + obj, exists, err := kd.servicesStore.GetByKey(key) + if err != nil { + return nil, fmt.Errorf("failed to get service object from services store - %v", err) + } + if !exists { + glog.V(1).Infof("could not find service for endpoint %q in namespace %q", e.Name, e.Namespace) + return nil, nil + } + if svc, ok := obj.(*kapi.Service); ok { + return svc, nil + } + return nil, fmt.Errorf("got a non service object in services store %v", obj) +} + +func (kd *KubeDNS) newPortalService(service *kapi.Service) { + subCache := NewTreeCache() + recordValue, recordLabel := getSkyMsg(service.Spec.ClusterIP, 0) + subCache.SetEntry(recordLabel, recordValue) + + // Generate SRV Records + for i := range service.Spec.Ports { + port := &service.Spec.Ports[i] + if port.Name != "" && port.Protocol != "" { + srvValue := kd.generateSRVRecordValue(service, int(port.Port)) + subCache.SetEntry(recordLabel, srvValue, "_"+strings.ToLower(string(port.Protocol)), "_"+port.Name) + } + } + subCachePath := append(kd.domainPath, serviceSubdomain, service.Namespace) + kd.cache.SetSubCache(service.Name, subCache, subCachePath...) +} + +func (kd *KubeDNS) generateRecordsForHeadlessService(e *kapi.Endpoints, svc *kapi.Service) error { + // TODO: remove this after v1.4 is released and the old annotations are EOL + podHostnames, err := getPodHostnamesFromAnnotation(e.Annotations) + if err != nil { + return err + } + subCache := NewTreeCache() + glog.V(4).Infof("Endpoints Annotations: %v", e.Annotations) + for idx := range e.Subsets { + for subIdx := range e.Subsets[idx].Addresses { + address := &e.Subsets[idx].Addresses[subIdx] + endpointIP := address.IP + recordValue, endpointName := getSkyMsg(endpointIP, 0) + if hostLabel, exists := getHostname(address, podHostnames); exists { + endpointName = hostLabel + } + subCache.SetEntry(endpointName, recordValue) + for portIdx := range e.Subsets[idx].Ports { + endpointPort := &e.Subsets[idx].Ports[portIdx] + if endpointPort.Name != "" && endpointPort.Protocol != "" { + srvValue := kd.generateSRVRecordValue(svc, int(endpointPort.Port), endpointName) + subCache.SetEntry(endpointName, srvValue, "_"+strings.ToLower(string(endpointPort.Protocol)), "_"+endpointPort.Name) + } + } + } + } + subCachePath := append(kd.domainPath, serviceSubdomain, svc.Namespace) + kd.cache.SetSubCache(svc.Name, subCache, subCachePath...) + return nil +} + +func getHostname(address *kapi.EndpointAddress, podHostnames map[string]endpoints.HostRecord) (string, bool) { + if len(address.Hostname) > 0 { + return address.Hostname, true + } + if hostRecord, exists := podHostnames[address.IP]; exists && validation.IsDNS1123Label(hostRecord.HostName) { + return hostRecord.HostName, true + } + return "", false +} + +func getPodHostnamesFromAnnotation(annotations map[string]string) (map[string]endpoints.HostRecord, error) { + hostnames := map[string]endpoints.HostRecord{} + + if annotations != nil { + if serializedHostnames, exists := annotations[endpoints.PodHostnamesAnnotation]; exists && len(serializedHostnames) > 0 { + err := json.Unmarshal([]byte(serializedHostnames), &hostnames) + if err != nil { + return nil, err + } + } + } + return hostnames, nil +} + +func (kd *KubeDNS) generateSRVRecordValue(svc *kapi.Service, portNumber int, cNameLabels ...string) *skymsg.Service { + cName := strings.Join([]string{svc.Name, svc.Namespace, serviceSubdomain, kd.domain}, ".") + for _, cNameLabel := range cNameLabels { + cName = cNameLabel + "." + cName + } + recordValue, _ := getSkyMsg(cName, portNumber) + return recordValue +} + +// Generates skydns records for a headless service. +func (kd *KubeDNS) newHeadlessService(service *kapi.Service) error { + // Create an A record for every pod in the service. + // This record must be periodically updated. + // Format is as follows: + // For a service x, with pods a and b create DNS records, + // a.x.ns.domain. and, b.x.ns.domain. + key, err := kcache.MetaNamespaceKeyFunc(service) + if err != nil { + return err + } + e, exists, err := kd.endpointsStore.GetByKey(key) + if err != nil { + return fmt.Errorf("failed to get endpoints object from endpoints store - %v", err) + } + if !exists { + glog.V(1).Infof("Could not find endpoints for service %q in namespace %q. DNS records will be created once endpoints show up.", service.Name, service.Namespace) + return nil + } + if e, ok := e.(*kapi.Endpoints); ok { + return kd.generateRecordsForHeadlessService(e, service) + } + return nil +} + +func (kd *KubeDNS) Records(name string, exact bool) ([]skymsg.Service, error) { + glog.Infof("Received DNS Request:%s, exact:%v", name, exact) + trimmed := strings.TrimRight(name, ".") + segments := strings.Split(trimmed, ".") + path := reverseArray(segments) + if kd.isPodRecord(path) { + response, err := kd.getPodRecord(path) + if err == nil { + return []skymsg.Service{*response}, nil + } + return nil, err + } + + if exact { + key := path[len(path)-1] + if key == "" { + return []skymsg.Service{}, nil + } + if record, ok := kd.cache.GetEntry(key, path[:len(path)-1]...); ok { + return []skymsg.Service{*(record.(*skymsg.Service))}, nil + } + return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound} + } + + // tmp, _ := kd.cache.Serialize("") + // glog.Infof("Searching path:%q, %v", path, tmp) + records := kd.cache.GetValuesForPathWithRegex(path...) + retval := []skymsg.Service{} + for _, val := range records { + retval = append(retval, *(val.(*skymsg.Service))) + } + glog.Infof("records:%v, retval:%v, path:%v", records, retval, path) + if len(retval) == 0 { + return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound} + } + return retval, nil +} + +func (kd *KubeDNS) ReverseRecord(name string) (*skymsg.Service, error) { + glog.Infof("Received ReverseRecord Request:%s", name) + + segments := strings.Split(strings.TrimRight(name, "."), ".") + + for _, k := range segments { + if k == "*" || k == "any" { + return nil, fmt.Errorf("reverse can not contain wildcards") + } + } + + return nil, fmt.Errorf("must be exactly one service record") +} + +// e.g {"local", "cluster", "pod", "default", "10-0-0-1"} +func (kd *KubeDNS) isPodRecord(path []string) bool { + if len(path) != len(kd.domainPath)+3 { + return false + } + if path[len(kd.domainPath)] != "pod" { + return false + } + for _, segment := range path { + if segment == "*" { + return false + } + } + return true +} + +func (kd *KubeDNS) getPodRecord(path []string) (*skymsg.Service, error) { + ipStr := path[len(path)-1] + ip := strings.Replace(ipStr, "-", ".", -1) + if parsed := net.ParseIP(ip); parsed != nil { + msg := &skymsg.Service{ + Host: ip, + Port: 0, + Priority: 10, + Weight: 10, + Ttl: 30, + } + return msg, nil + } + return nil, fmt.Errorf("Invalid IP Address %v", ip) +} + +// Returns record in a format that SkyDNS understands. +// Also return the hash of the record. +func getSkyMsg(ip string, port int) (*skymsg.Service, string) { + msg := &skymsg.Service{ + Host: ip, + Port: port, + Priority: 10, + Weight: 10, + Ttl: 30, + } + s := fmt.Sprintf("%v", msg) + h := fnv.New32a() + h.Write([]byte(s)) + hash := fmt.Sprintf("%x", h.Sum32()) + glog.Infof("DNS Record:%s, hash:%s", s, hash) + return msg, fmt.Sprintf("%x", hash) +} + +func reverseArray(arr []string) []string { + for i := 0; i < len(arr)/2; i++ { + j := len(arr) - i - 1 + arr[i], arr[j] = arr[j], arr[i] + } + return arr +} diff --git a/pkg/dns/dns_test.go b/pkg/dns/dns_test.go new file mode 100644 index 00000000000..9a7a6e61e90 --- /dev/null +++ b/pkg/dns/dns_test.go @@ -0,0 +1,377 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dns + +import ( + "fmt" + "strings" + "testing" + + skymsg "github.com/skynetservices/skydns/msg" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + "net" +) + +const ( + testDomain = "cluster.local." + basePath = "/skydns/local/cluster" + serviceSubDomain = "svc" + podSubDomain = "pod" + testService = "testservice" + testNamespace = "default" +) + +func newKubeDNS() *KubeDNS { + kd := &KubeDNS{ + domain: testDomain, + endpointsStore: cache.NewStore(cache.MetaNamespaceKeyFunc), + servicesStore: cache.NewStore(cache.MetaNamespaceKeyFunc), + cache: NewTreeCache(), + domainPath: reverseArray(strings.Split(strings.TrimRight(testDomain, "."), ".")), + } + return kd +} + +func TestPodDns(t *testing.T) { + const ( + testPodIP = "1.2.3.4" + sanitizedPodIP = "1-2-3-4" + testPodName = "testPod" + ) + kd := newKubeDNS() + + records, err := kd.Records(sanitizedPodIP+".default.pod."+kd.domain, false) + require.NoError(t, err) + assert.Equal(t, 1, len(records)) + assert.Equal(t, testPodIP, records[0].Host) +} + +func TestUnnamedSinglePortService(t *testing.T) { + kd := newKubeDNS() + s := newService(testNamespace, testService, "1.2.3.4", "", 80) + // Add the service + kd.newService(s) + assertDNSForClusterIP(t, kd, s) + // Delete the service + kd.removeService(s) + assertNoDNSForClusterIP(t, kd, s) +} + +func TestNamedSinglePortService(t *testing.T) { + const ( + portName1 = "http1" + portName2 = "http2" + ) + kd := newKubeDNS() + s := newService(testNamespace, testService, "1.2.3.4", portName1, 80) + // Add the service + kd.newService(s) + assertDNSForClusterIP(t, kd, s) + assertSRVForNamedPort(t, kd, s, portName1) + + newService := *s + // update the portName of the service + newService.Spec.Ports[0].Name = portName2 + kd.updateService(s, &newService) + assertDNSForClusterIP(t, kd, s) + assertSRVForNamedPort(t, kd, s, portName2) + assertNoSRVForNamedPort(t, kd, s, portName1) + + // Delete the service + kd.removeService(s) + assertNoDNSForClusterIP(t, kd, s) + assertNoSRVForNamedPort(t, kd, s, portName1) + assertNoSRVForNamedPort(t, kd, s, portName2) +} + +func TestHeadlessService(t *testing.T) { + kd := newKubeDNS() + s := newHeadlessService() + assert.NoError(t, kd.servicesStore.Add(s)) + endpoints := newEndpoints(s, newSubsetWithOnePort("", 80, "10.0.0.1", "10.0.0.2"), newSubsetWithOnePort("", 8080, "10.0.0.3", "10.0.0.4")) + + assert.NoError(t, kd.endpointsStore.Add(endpoints)) + kd.newService(s) + assertDNSForHeadlessService(t, kd, endpoints) + kd.removeService(s) + assertNoDNSForHeadlessService(t, kd, s) +} + +func TestHeadlessServiceWithNamedPorts(t *testing.T) { + kd := newKubeDNS() + service := newHeadlessService() + // add service to store + assert.NoError(t, kd.servicesStore.Add(service)) + endpoints := newEndpoints(service, newSubsetWithTwoPorts("http1", 80, "http2", 81, "10.0.0.1", "10.0.0.2"), + newSubsetWithOnePort("https", 443, "10.0.0.3", "10.0.0.4")) + + // We expect 10 records. 6 SRV records. 4 POD records. + // add endpoints + assert.NoError(t, kd.endpointsStore.Add(endpoints)) + + // add service + kd.newService(service) + assertDNSForHeadlessService(t, kd, endpoints) + assertSRVForHeadlessService(t, kd, service, endpoints) + + // reduce endpoints + endpoints.Subsets = endpoints.Subsets[:1] + kd.handleEndpointAdd(endpoints) + // We expect 6 records. 4 SRV records. 2 POD records. + assertDNSForHeadlessService(t, kd, endpoints) + assertSRVForHeadlessService(t, kd, service, endpoints) + + kd.removeService(service) + assertNoDNSForHeadlessService(t, kd, service) +} + +func TestHeadlessServiceEndpointsUpdate(t *testing.T) { + kd := newKubeDNS() + service := newHeadlessService() + // add service to store + assert.NoError(t, kd.servicesStore.Add(service)) + + endpoints := newEndpoints(service, newSubsetWithOnePort("", 80, "10.0.0.1", "10.0.0.2")) + // add endpoints to store + assert.NoError(t, kd.endpointsStore.Add(endpoints)) + + // add service + kd.newService(service) + assertDNSForHeadlessService(t, kd, endpoints) + + // increase endpoints + endpoints.Subsets = append(endpoints.Subsets, + newSubsetWithOnePort("", 8080, "10.0.0.3", "10.0.0.4"), + ) + // expected DNSRecords = 4 + kd.handleEndpointAdd(endpoints) + assertDNSForHeadlessService(t, kd, endpoints) + + // remove all endpoints + endpoints.Subsets = []kapi.EndpointSubset{} + kd.handleEndpointAdd(endpoints) + assertNoDNSForHeadlessService(t, kd, service) + + // remove service + kd.removeService(service) + assertNoDNSForHeadlessService(t, kd, service) +} + +func TestHeadlessServiceWithDelayedEndpointsAddition(t *testing.T) { + kd := newKubeDNS() + // create service + service := newHeadlessService() + + // add service to store + assert.NoError(t, kd.servicesStore.Add(service)) + + // add service + kd.newService(service) + assertNoDNSForHeadlessService(t, kd, service) + + // create endpoints + endpoints := newEndpoints(service, newSubsetWithOnePort("", 80, "10.0.0.1", "10.0.0.2")) + + // add endpoints to store + assert.NoError(t, kd.endpointsStore.Add(endpoints)) + + // add endpoints + kd.handleEndpointAdd(endpoints) + + assertDNSForHeadlessService(t, kd, endpoints) + + // remove service + kd.removeService(service) + assertNoDNSForHeadlessService(t, kd, service) +} + +func newService(namespace, serviceName, clusterIP, portName string, portNumber int32) *kapi.Service { + service := kapi.Service{ + ObjectMeta: kapi.ObjectMeta{ + Name: serviceName, + Namespace: namespace, + }, + Spec: kapi.ServiceSpec{ + ClusterIP: clusterIP, + Ports: []kapi.ServicePort{ + {Port: portNumber, Name: portName, Protocol: "TCP"}, + }, + }, + } + return &service +} + +func newHeadlessService() *kapi.Service { + service := kapi.Service{ + ObjectMeta: kapi.ObjectMeta{ + Name: testService, + Namespace: testNamespace, + }, + Spec: kapi.ServiceSpec{ + ClusterIP: "None", + Ports: []kapi.ServicePort{ + {Port: 0}, + }, + }, + } + return &service +} + +func newEndpoints(service *kapi.Service, subsets ...kapi.EndpointSubset) *kapi.Endpoints { + endpoints := kapi.Endpoints{ + ObjectMeta: service.ObjectMeta, + Subsets: []kapi.EndpointSubset{}, + } + + for _, subset := range subsets { + endpoints.Subsets = append(endpoints.Subsets, subset) + } + return &endpoints +} + +func newSubsetWithOnePort(portName string, port int32, ips ...string) kapi.EndpointSubset { + subset := newSubset() + subset.Ports = append(subset.Ports, kapi.EndpointPort{Port: port, Name: portName, Protocol: "TCP"}) + for _, ip := range ips { + subset.Addresses = append(subset.Addresses, kapi.EndpointAddress{IP: ip}) + } + return subset +} + +func newSubsetWithTwoPorts(portName1 string, portNumber1 int32, portName2 string, portNumber2 int32, ips ...string) kapi.EndpointSubset { + subset := newSubsetWithOnePort(portName1, portNumber1, ips...) + subset.Ports = append(subset.Ports, kapi.EndpointPort{Port: portNumber2, Name: portName2, Protocol: "TCP"}) + return subset +} + +func newSubset() kapi.EndpointSubset { + subset := kapi.EndpointSubset{ + Addresses: []kapi.EndpointAddress{}, + Ports: []kapi.EndpointPort{}, + } + return subset +} + +func assertSRVForHeadlessService(t *testing.T, kd *KubeDNS, s *kapi.Service, e *kapi.Endpoints) { + for _, subset := range e.Subsets { + for _, port := range subset.Ports { + records, err := kd.Records(getSRVFQDN(kd, s, port.Name), false) + require.NoError(t, err) + assertRecordPortsMatchPort(t, port.Port, records) + assertCNameRecordsMatchEndpointIPs(t, kd, subset.Addresses, records) + } + } +} + +func assertDNSForHeadlessService(t *testing.T, kd *KubeDNS, e *kapi.Endpoints) { + records, err := kd.Records(getEndpointsFQDN(kd, e), false) + require.NoError(t, err) + endpoints := map[string]bool{} + for _, subset := range e.Subsets { + for _, endpointAddress := range subset.Addresses { + endpoints[endpointAddress.IP] = true + } + } + assert.Equal(t, len(endpoints), len(records)) + for _, record := range records { + _, found := endpoints[record.Host] + assert.True(t, found) + } +} + +func assertRecordPortsMatchPort(t *testing.T, port int32, records []skymsg.Service) { + for _, record := range records { + assert.Equal(t, port, int32(record.Port)) + } +} + +func assertCNameRecordsMatchEndpointIPs(t *testing.T, kd *KubeDNS, e []kapi.EndpointAddress, records []skymsg.Service) { + endpoints := map[string]bool{} + for _, endpointAddress := range e { + endpoints[endpointAddress.IP] = true + } + assert.Equal(t, len(e), len(records), "unexpected record count") + for _, record := range records { + _, found := endpoints[getIPForCName(t, kd, record.Host)] + assert.True(t, found, "Did not endpoint with address:%s", record.Host) + } +} + +func getIPForCName(t *testing.T, kd *KubeDNS, cname string) string { + records, err := kd.Records(cname, false) + require.NoError(t, err) + assert.Equal(t, 1, len(records), "Could not get IP for CNAME record for %s", cname) + assert.NotNil(t, net.ParseIP(records[0].Host), "Invalid IP address %q", records[0].Host) + return records[0].Host +} + +func assertNoDNSForHeadlessService(t *testing.T, kd *KubeDNS, s *kapi.Service) { + records, err := kd.Records(getServiceFQDN(kd, s), false) + require.Error(t, err) + assert.Equal(t, 0, len(records)) +} + +func assertSRVForNamedPort(t *testing.T, kd *KubeDNS, s *kapi.Service, portName string) { + records, err := kd.Records(getSRVFQDN(kd, s, portName), false) + require.NoError(t, err) + assert.Equal(t, 1, len(records)) + assert.Equal(t, getServiceFQDN(kd, s), records[0].Host) +} + +func assertNoSRVForNamedPort(t *testing.T, kd *KubeDNS, s *kapi.Service, portName string) { + records, err := kd.Records(getSRVFQDN(kd, s, portName), false) + require.Error(t, err) + assert.Equal(t, 0, len(records)) +} + +func assertNoDNSForClusterIP(t *testing.T, kd *KubeDNS, s *kapi.Service) { + records, err := kd.Records(getServiceFQDN(kd, s), false) + require.Error(t, err) + assert.Equal(t, 0, len(records)) +} + +func assertDNSForClusterIP(t *testing.T, kd *KubeDNS, s *kapi.Service) { + serviceFQDN := getServiceFQDN(kd, s) + queries := []string{ + serviceFQDN, + strings.Replace(serviceFQDN, ".svc.", ".*.", 1), + strings.Replace(serviceFQDN, s.Namespace, "*", 1), + strings.Replace(strings.Replace(serviceFQDN, s.Namespace, "*", 1), ".svc.", ".*.", 1), + "*." + serviceFQDN, + } + for _, query := range queries { + records, err := kd.Records(query, false) + require.NoError(t, err) + assert.Equal(t, 1, len(records)) + assert.Equal(t, s.Spec.ClusterIP, records[0].Host) + } +} + +func getServiceFQDN(kd *KubeDNS, s *kapi.Service) string { + return fmt.Sprintf("%s.%s.svc.%s", s.Name, s.Namespace, kd.domain) +} + +func getEndpointsFQDN(kd *KubeDNS, e *kapi.Endpoints) string { + return fmt.Sprintf("%s.%s.svc.%s", e.ObjectMeta.Name, e.ObjectMeta.Namespace, kd.domain) +} + +func getSRVFQDN(kd *KubeDNS, s *kapi.Service, portName string) string { + return fmt.Sprintf("_%s._tcp.%s.%s.svc.%s", portName, s.Name, s.Namespace, kd.domain) +} diff --git a/pkg/dns/treecache.go b/pkg/dns/treecache.go new file mode 100644 index 00000000000..ee3ba206ee7 --- /dev/null +++ b/pkg/dns/treecache.go @@ -0,0 +1,312 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dns + +import ( + "bytes" + "crypto/md5" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "reflect" + "strings" + "sync" +) + +const ( + dataFile = "data.dat" + crcFile = "data.crc" +) + +type object interface{} + +type TreeCache struct { + ChildNodes map[string]*TreeCache + Entries map[string]interface{} + m *sync.RWMutex +} + +func NewTreeCache() *TreeCache { + return &TreeCache{ + ChildNodes: make(map[string]*TreeCache), + Entries: make(map[string]interface{}), + m: &sync.RWMutex{}, + } +} + +func Deserialize(dir string) (*TreeCache, error) { + b, err := ioutil.ReadFile(path.Join(dir, dataFile)) + if err != nil { + return nil, err + } + var hash []byte + hash, err = ioutil.ReadFile(path.Join(dir, crcFile)) + if err != nil { + return nil, err + } + if !reflect.DeepEqual(hash, getMD5(b)) { + return nil, fmt.Errorf("Checksum failed") + } + + var cache TreeCache + err = json.Unmarshal(b, &cache) + if err != nil { + return nil, err + } + cache.m = &sync.RWMutex{} + return &cache, nil +} + +func (cache *TreeCache) Serialize(dir string) (string, error) { + cache.m.RLock() + defer cache.m.RUnlock() + b, err := json.Marshal(cache) + if err != nil { + return "", err + } + + if len(dir) == 0 { + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, b, "", "\t") + + if err != nil { + return "", err + } + return string(prettyJSON.Bytes()), nil + } + if err := ensureDir(dir, os.FileMode(0755)); err != nil { + return "", err + } + if err := ioutil.WriteFile(path.Join(dir, dataFile), b, 0644); err != nil { + return "", err + } + if err := ioutil.WriteFile(path.Join(dir, crcFile), getMD5(b), 0644); err != nil { + return "", err + } + return string(b), nil +} + +func (cache *TreeCache) SetEntry(key string, val interface{}, path ...string) { + cache.m.Lock() + defer cache.m.Unlock() + node := cache.ensureChildNode(path...) + node.Entries[key] = val +} + +func (cache *TreeCache) ReplaceEntries(entries map[string]interface{}, path ...string) { + cache.m.Lock() + defer cache.m.Unlock() + node := cache.ensureChildNode(path...) + node.Entries = make(map[string]interface{}) + for key, val := range entries { + node.Entries[key] = val + } +} + +func (cache *TreeCache) GetSubCache(path ...string) *TreeCache { + childCache := cache + for _, subpath := range path { + childCache = childCache.ChildNodes[subpath] + if childCache == nil { + return childCache + } + } + return childCache +} + +func (cache *TreeCache) SetSubCache(key string, subCache *TreeCache, path ...string) { + cache.m.Lock() + defer cache.m.Unlock() + node := cache.ensureChildNode(path...) + node.ChildNodes[key] = subCache +} + +func (cache *TreeCache) GetEntry(key string, path ...string) (interface{}, bool) { + cache.m.RLock() + defer cache.m.RUnlock() + childNode := cache.GetSubCache(path...) + val, ok := childNode.Entries[key] + return val, ok +} + +func (cache *TreeCache) GetValuesForPathWithRegex(path ...string) []interface{} { + cache.m.RLock() + defer cache.m.RUnlock() + retval := []interface{}{} + nodesToExplore := []*TreeCache{cache} + for idx, subpath := range path { + nextNodesToExplore := []*TreeCache{} + if idx == len(path)-1 { + // if path ends on an entry, instead of a child node, add the entry + for _, node := range nodesToExplore { + if subpath == "*" || subpath == "any" { + nextNodesToExplore = append(nextNodesToExplore, node) + } else { + if val, ok := node.Entries[subpath]; ok { + retval = append(retval, val) + } else { + childNode := node.ChildNodes[subpath] + if childNode != nil { + nextNodesToExplore = append(nextNodesToExplore, childNode) + } + } + } + } + nodesToExplore = nextNodesToExplore + break + } + + if subpath == "*" || subpath == "any" { + for _, node := range nodesToExplore { + for subkey, subnode := range node.ChildNodes { + if !strings.HasPrefix(subkey, "_") { + nextNodesToExplore = append(nextNodesToExplore, subnode) + } + } + } + } else { + for _, node := range nodesToExplore { + childNode := node.ChildNodes[subpath] + if childNode != nil { + nextNodesToExplore = append(nextNodesToExplore, childNode) + } + } + } + nodesToExplore = nextNodesToExplore + } + + for _, node := range nodesToExplore { + for _, val := range node.Entries { + retval = append(retval, val) + } + } + + return retval +} + +func (cache *TreeCache) GetEntries(recursive bool, path ...string) []interface{} { + cache.m.RLock() + defer cache.m.RUnlock() + childNode := cache.GetSubCache(path...) + if childNode == nil { + return nil + } + + retval := [][]interface{}{{}} + childNode.appendValues(recursive, retval) + return retval[0] +} + +func (cache *TreeCache) DeletePath(path ...string) bool { + if len(path) == 0 { + return false + } + cache.m.Lock() + defer cache.m.Unlock() + if parentNode := cache.GetSubCache(path[:len(path)-1]...); parentNode != nil { + if _, ok := parentNode.ChildNodes[path[len(path)-1]]; ok { + delete(parentNode.ChildNodes, path[len(path)-1]) + return true + } + } + return false +} + +func (tn *TreeCache) DeleteEntry(key string, path ...string) bool { + tn.m.Lock() + defer tn.m.Unlock() + childNode := tn.GetSubCache(path...) + if childNode == nil { + return false + } + if _, ok := childNode.Entries[key]; ok { + delete(childNode.Entries, key) + return true + } + return false +} + +func (tn *TreeCache) appendValues(recursive bool, ref [][]interface{}) { + for _, value := range tn.Entries { + ref[0] = append(ref[0], value) + } + if recursive { + for _, node := range tn.ChildNodes { + node.appendValues(recursive, ref) + } + } +} + +func (tn *TreeCache) ensureChildNode(path ...string) *TreeCache { + childNode := tn + for _, subpath := range path { + newNode := childNode.ChildNodes[subpath] + if newNode == nil { + newNode = NewTreeCache() + childNode.ChildNodes[subpath] = newNode + } + childNode = newNode + } + return childNode +} + +func ensureDir(path string, perm os.FileMode) error { + s, err := os.Stat(path) + if err != nil || !s.IsDir() { + return os.Mkdir(path, perm) + } + return nil +} + +func getMD5(b []byte) []byte { + h := md5.New() + h.Write(b) + return []byte(fmt.Sprintf("%x", h.Sum(nil))) +} + +func main() { + root := NewTreeCache() + fmt.Println("Adding Entries") + root.SetEntry("k", "v") + root.SetEntry("foo", "bar", "local") + root.SetEntry("foo1", "bar1", "local", "cluster") + + fmt.Println("Fetching Entries") + for _, entry := range root.GetEntries(true, "local") { + fmt.Printf("%s\n", entry) + } + + fmt.Println("Serializing") + if _, err := root.Serialize("./foo"); err != nil { + fmt.Printf("Serialization Error: %v,\n", err) + return + } + + fmt.Println("Deserializing") + tn, err := Deserialize("./foo") + if err != nil { + fmt.Printf("Deserialization Error: %v\n", err) + return + } + + fmt.Println("Fetching Entries") + for _, entry := range tn.GetEntries(true, "local") { + fmt.Printf("%s\n", entry) + } +} From e5dd1c4c0a60136876853297bedd0e93d7e0c0d8 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Wed, 4 May 2016 16:24:43 -0700 Subject: [PATCH 3/6] added cmd/kube-dns --- cmd/kube-dns/app/options/options.go | 103 ++++++++++++++++++++++ cmd/kube-dns/app/server.go | 131 ++++++++++++++++++++++++++++ cmd/kube-dns/dns.go | 41 +++++++++ 3 files changed, 275 insertions(+) create mode 100644 cmd/kube-dns/app/options/options.go create mode 100644 cmd/kube-dns/app/server.go create mode 100644 cmd/kube-dns/dns.go diff --git a/cmd/kube-dns/app/options/options.go b/cmd/kube-dns/app/options/options.go new file mode 100644 index 00000000000..973a24901a9 --- /dev/null +++ b/cmd/kube-dns/app/options/options.go @@ -0,0 +1,103 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package options contains flags for initializing a proxy. +package options + +import ( + "net/url" + "os" + + "fmt" + "github.com/spf13/pflag" + "k8s.io/kubernetes/pkg/util/validation" + _ "net/http/pprof" + "strings" +) + +type KubeDNSConfig struct { + ClusterDomain string + KubeConfigFile string + KubeMasterURL string + HealthzPort int +} + +func NewKubeDNSConfig() *KubeDNSConfig { + return &KubeDNSConfig{ + ClusterDomain: "cluster.local.", + KubeConfigFile: "", + KubeMasterURL: "", + HealthzPort: 8081, + } +} + +type clusterDomainVar struct { + val *string +} + +func (m clusterDomainVar) Set(v string) error { + v = strings.TrimSuffix(v, ".") + segments := strings.Split(v, ".") + for _, segment := range segments { + if !validation.IsDNS1123Label(segment) { + return fmt.Errorf("Not a valid DNS label") + } + } + if !strings.HasSuffix(v, ".") { + v = fmt.Sprintf("%s.", v) + } + *m.val = v + return nil +} + +func (m clusterDomainVar) String() string { + return *m.val +} + +func (m clusterDomainVar) Type() string { + return "string" +} + +type kubeMasterURLVar struct { + val *string +} + +func (m kubeMasterURLVar) Set(v string) error { + parsedURL, err := url.Parse(os.ExpandEnv(v)) + if err != nil { + return fmt.Errorf("failed to parse kube-master-url") + } + if parsedURL.Scheme == "" || parsedURL.Host == "" || parsedURL.Host == ":" { + return fmt.Errorf("invalid kube-master-url specified") + } + *m.val = v + return nil +} + +func (m kubeMasterURLVar) String() string { + return *m.val +} + +func (m kubeMasterURLVar) Type() string { + return "string" +} + +func (s *KubeDNSConfig) AddFlags(fs *pflag.FlagSet) { + fs.Var(clusterDomainVar{&s.ClusterDomain}, "domain", "domain under which to create names") + fs.StringVar(&s.KubeConfigFile, "kubecfg-file", s.KubeConfigFile, "Location of kubecfg file for access to kubernetes master service; --kube-master-url overrides the URL part of this; if neither this nor --kube-master-url are provided, defaults to service account tokens") + fs.Var(kubeMasterURLVar{&s.KubeMasterURL}, "kube-master-url", "URL to reach kubernetes master. Env variables in this flag will be expanded.") + fs.IntVar(&s.HealthzPort, "healthz-port", s.HealthzPort, "port on which to serve a kube-dns HTTP readiness probe.") +} diff --git a/cmd/kube-dns/app/server.go b/cmd/kube-dns/app/server.go new file mode 100644 index 00000000000..d7d3d20f1d8 --- /dev/null +++ b/cmd/kube-dns/app/server.go @@ -0,0 +1,131 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/golang/glog" + + "github.com/skynetservices/skydns/metrics" + "github.com/skynetservices/skydns/server" + "k8s.io/kubernetes/cmd/kube-dns/app/options" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" + kclient "k8s.io/kubernetes/pkg/client/unversioned" + kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + kdns "k8s.io/kubernetes/pkg/dns" +) + +type KubeDNSServer struct { + // DNS domain name. + domain string + healthzPort int + kd *kdns.KubeDNS +} + +func NewKubeDNSServerDefault(config *options.KubeDNSConfig) *KubeDNSServer { + ks := KubeDNSServer{ + domain: config.ClusterDomain, + } + + kubeClient, err := newKubeClient(config) + if err != nil { + glog.Fatalf("Failed to create a kubernetes client: %v", err) + } + ks.healthzPort = config.HealthzPort + ks.kd = kdns.NewKubeDNS(kubeClient, config.ClusterDomain) + return &ks +} + +// TODO: evaluate using pkg/client/clientcmd +func newKubeClient(dnsConfig *options.KubeDNSConfig) (*kclient.Client, error) { + var ( + config *restclient.Config + err error + ) + + if dnsConfig.KubeMasterURL != "" && dnsConfig.KubeConfigFile == "" { + // Only --kube-master-url was provided. + config = &restclient.Config{ + Host: dnsConfig.KubeMasterURL, + ContentConfig: restclient.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: "v1"}}, + } + } else { + // We either have: + // 1) --kube-master-url and --kubecfg-file + // 2) just --kubecfg-file + // 3) neither flag + // In any case, the logic is the same. If (3), this will automatically + // fall back on the service account token. + overrides := &kclientcmd.ConfigOverrides{} + overrides.ClusterInfo.Server = dnsConfig.KubeMasterURL // might be "", but that is OK + rules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: dnsConfig.KubeConfigFile} // might be "", but that is OK + if config, err = kclientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig(); err != nil { + return nil, err + } + } + + glog.Infof("Using %s for kubernetes master", config.Host) + glog.Infof("Using kubernetes API %v", config.GroupVersion) + return kclient.New(config) +} + +func (server *KubeDNSServer) Run() { + setupSignalHandlers() + server.startSkyDNSServer() + server.kd.Start() + server.setupHealthzHandlers() + glog.Infof("Setting up Healthz Handler(/readiness, /cache) on port :%d", server.healthzPort) + glog.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", server.healthzPort), nil)) +} + +// setupHealthzHandlers sets up a readiness and liveness endpoint for kube2sky. +func (server *KubeDNSServer) setupHealthzHandlers() { + http.HandleFunc("/readiness", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "ok\n") + }) + http.HandleFunc("/cache", func(w http.ResponseWriter, req *http.Request) { + fmt.Fprint(w, server.kd.GetCacheAsJSON()) + }) +} + +// setupSignalHandlers runs a goroutine that waits on SIGINT or SIGTERM and logs it +// before exiting. +func setupSignalHandlers() { + sigChan := make(chan os.Signal) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + go func() { + glog.Fatalf("Received signal: %s", <-sigChan) + }() +} + +func (d *KubeDNSServer) startSkyDNSServer() { + skydnsConfig := &server.Config{Domain: d.domain, DnsAddr: "0.0.0.0:53"} + server.SetDefaults(skydnsConfig) + s := server.New(d.kd, skydnsConfig) + if err := metrics.Metrics(); err != nil { + glog.Fatalf("skydns: %s", err) + } else { + glog.Infof("skydns: metrics enabled on :%s%s", metrics.Port, metrics.Path) + } + go s.Run() +} diff --git a/cmd/kube-dns/dns.go b/cmd/kube-dns/dns.go new file mode 100644 index 00000000000..07d4661d855 --- /dev/null +++ b/cmd/kube-dns/dns.go @@ -0,0 +1,41 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/pflag" + "k8s.io/kubernetes/cmd/kube-dns/app" + "k8s.io/kubernetes/cmd/kube-dns/app/options" + "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/flag" + "k8s.io/kubernetes/pkg/version/verflag" + "runtime" +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + config := options.NewKubeDNSConfig() + config.AddFlags(pflag.CommandLine) + + flag.InitFlags() + util.InitLogs() + defer util.FlushLogs() + + verflag.PrintAndExitIfRequested() + server := app.NewKubeDNSServerDefault(config) + server.Run() +} From a92ea560241bd369591aa1f43b0c5057b8d8da8c Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Wed, 4 May 2016 16:29:22 -0700 Subject: [PATCH 4/6] added build stuff for kube-dns --- build/kube-dns/Dockerfile | 18 ++++ build/kube-dns/Makefile | 66 +++++++++++++++ cluster/saltbase/salt/kube-addons/init.sls | 8 +- .../saltbase/salt/kube-dns/kubedns-rc.yaml.in | 83 +++++++++++++++++++ .../salt/kube-dns/kubedns-svc.yaml.in | 20 +++++ hack/lib/golang.sh | 2 + 6 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 build/kube-dns/Dockerfile create mode 100644 build/kube-dns/Makefile create mode 100644 cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in create mode 100644 cluster/saltbase/salt/kube-dns/kubedns-svc.yaml.in diff --git a/build/kube-dns/Dockerfile b/build/kube-dns/Dockerfile new file mode 100644 index 00000000000..6e72d95a750 --- /dev/null +++ b/build/kube-dns/Dockerfile @@ -0,0 +1,18 @@ +# Copyright 2016 The Kubernetes Authors All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM BASEIMAGE +MAINTAINER Tim Hockin +ADD kube-dns / +ENTRYPOINT ["/kube-dns"] diff --git a/build/kube-dns/Makefile b/build/kube-dns/Makefile new file mode 100644 index 00000000000..86b3a669e4d --- /dev/null +++ b/build/kube-dns/Makefile @@ -0,0 +1,66 @@ +# Copyright 2016 The Kubernetes Authors All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Makefile for the Docker image gcr.io/google_containers/kube2sky +# MAINTAINER: Tim Hockin +# If you update this image please bump the tag value before pushing. +# +# Usage: +# [ARCH=amd64] [TAG=1.0] [REGISTRY=gcr.io/google_containers] [BASEIMAGE=busybox] make container + +# Default registry, arch and tag. This can be overwritten by arguments to make +PLATFORM?=linux +ARCH?=amd64 +TAG?=1.0 +REGISTRY?=gcr.io/google_containers + +GOLANG_VERSION=1.6 +GOARM=6 +KUBE_ROOT=$(shell pwd)/../.. +TEMP_DIR:=$(shell mktemp -d) + +ifeq ($(ARCH),amd64) + BASEIMAGE?=busybox +endif +ifeq ($(ARCH),arm) + BASEIMAGE?=armel/busybox +endif +ifeq ($(ARCH),arm64) + BASEIMAGE?=aarch64/busybox +endif +ifeq ($(ARCH),ppc64le) + BASEIMAGE?=ppc64le/busybox +endif + + +all: container + +container: + # Copy the content in this dir to the temp dir + cp $(KUBE_ROOT)/_output/local/bin/$(PLATFORM)/$(ARCH)/kube-dns $(TEMP_DIR) + cp $(KUBE_ROOT)/build/kube-dns/Dockerfile $(TEMP_DIR) + + # Replace BASEIMAGE with the real base image + cd $(TEMP_DIR) && sed -i "s|BASEIMAGE|$(BASEIMAGE)|g" Dockerfile + + # And build the image + docker build -t $(REGISTRY)/kubedns-$(ARCH):$(TAG) $(TEMP_DIR) + + # delete temp dir + rm -rf $(TEMP_DIR) + +push: container + gcloud docker push $(REGISTRY)/kubedns-$(ARCH):$(TAG) + +.PHONY: all container push diff --git a/cluster/saltbase/salt/kube-addons/init.sls b/cluster/saltbase/salt/kube-addons/init.sls index 39d845a91ac..4ea840218ad 100644 --- a/cluster/saltbase/salt/kube-addons/init.sls +++ b/cluster/saltbase/salt/kube-addons/init.sls @@ -73,17 +73,17 @@ addon-dir-create: {% endif %} {% if pillar.get('enable_cluster_dns', '').lower() == 'true' %} -/etc/kubernetes/addons/dns/skydns-svc.yaml: +/etc/kubernetes/addons/dns/kubedns-svc.yaml: file.managed: - - source: salt://kube-addons/dns/skydns-svc.yaml.in + - source: salt://kube-dns/kubedns-svc.yaml.in - template: jinja - group: root - dir_mode: 755 - makedirs: True -/etc/kubernetes/addons/dns/skydns-rc.yaml: +/etc/kubernetes/addons/dns/kubedns-rc.yaml: file.managed: - - source: salt://kube-addons/dns/skydns-rc.yaml.in + - source: salt://kube-dns/kubedns-rc.yaml.in - template: jinja - group: root - dir_mode: 755 diff --git a/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in b/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in new file mode 100644 index 00000000000..631939edc6f --- /dev/null +++ b/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in @@ -0,0 +1,83 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + name: kube-dns-v12 + namespace: kube-system + labels: + k8s-app: kube-dns + version: v12 + kubernetes.io/cluster-service: "true" +spec: + replicas: {{ pillar['dns_replicas'] }} + selector: + k8s-app: kube-dns + version: v12 + template: + metadata: + labels: + k8s-app: kube-dns + version: v12 + kubernetes.io/cluster-service: "true" + spec: +{% if grains['cloud'] is defined and grains['cloud'] in [ 'vsphere', 'photon-controller' ] %} + hostNetwork: true +{% endif %} + containers: + - name: kubedns + image: artfulcoder/kubedns-amd64:1.0 + resources: + # TODO: Set memory limits when we've profiled the container for large + # clusters, then set request = limit to keep this container in + # guaranteed class. Currently, this container falls into the + # "burstable" category so the kubelet doesn't backoff from restarting it. + limits: + cpu: 100m + memory: 200Mi + requests: + cpu: 100m + memory: 50Mi + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + httpGet: + path: /readiness + port: 8081 + scheme: HTTP + # we poll on pod startup for the Kubernetes master service and + # only setup the /readiness HTTP server once that's available. + initialDelaySeconds: 30 + timeoutSeconds: 5 + args: + # command = "/kube-dns" + - --domain={{ pillar['dns_domain'] }}. + ports: + - containerPort: 53 + name: dns + protocol: UDP + - containerPort: 53 + name: dns-tcp + protocol: TCP + - name: healthz + image: gcr.io/google_containers/exechealthz:1.0 + resources: + # keep request = limit to keep this container in guaranteed class + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi + args: + - -cmd=nslookup kubernetes.default.svc.{{ pillar['dns_domain'] }} 127.0.0.1 >/dev/null + - -port=8080 + ports: + - containerPort: 8080 + protocol: TCP + dnsPolicy: Default # Don't use cluster DNS. diff --git a/cluster/saltbase/salt/kube-dns/kubedns-svc.yaml.in b/cluster/saltbase/salt/kube-dns/kubedns-svc.yaml.in new file mode 100644 index 00000000000..242c8871eec --- /dev/null +++ b/cluster/saltbase/salt/kube-dns/kubedns-svc.yaml.in @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: kube-dns + namespace: kube-system + labels: + k8s-app: kube-dns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "KubeDNS" +spec: + selector: + k8s-app: kube-dns + clusterIP: {{ pillar['dns_server'] }} + ports: + - name: dns + port: 53 + protocol: UDP + - name: dns-tcp + port: 53 + protocol: TCP diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh index 1d9b21dde4e..9206cebfd8e 100755 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh @@ -30,6 +30,7 @@ fi # kube::build::source_targets in build/common.sh as well. kube::golang::server_targets() { local targets=( + cmd/kube-dns cmd/kube-proxy cmd/kube-apiserver cmd/kube-controller-manager @@ -159,6 +160,7 @@ readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}") readonly KUBE_STATIC_LIBRARIES=( kube-apiserver kube-controller-manager + kube-dns kube-scheduler kube-proxy kubectl From fc040645eb491d80f33befae3b0e3ca67e9a1053 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Thu, 12 May 2016 14:58:55 -0700 Subject: [PATCH 5/6] vendor-ed skydns --- Godeps/.license_file_state | 53 +- Godeps/Godeps.json | 25 + Godeps/LICENSES | 704 ++++++++++++++ .../coreos/go-systemd/activation/files.go | 52 + .../coreos/go-systemd/activation/listeners.go | 62 ++ .../go-systemd/activation/packetconns.go | 37 + .../skynetservices/skydns/cache/cache.go | 167 ++++ .../skynetservices/skydns/cache/hit.go | 31 + .../skynetservices/skydns/metrics/metrics.go | 186 ++++ .../skynetservices/skydns/server/backend.go | 46 + .../skynetservices/skydns/server/config.go | 157 ++++ .../skynetservices/skydns/server/dnssec.go | 177 ++++ .../skynetservices/skydns/server/doc.go | 8 + .../skynetservices/skydns/server/exchange.go | 34 + .../skydns/server/forwarding.go | 125 +++ .../skynetservices/skydns/server/log.go | 17 + .../skynetservices/skydns/server/msg.go | 54 ++ .../skynetservices/skydns/server/nsec3.go | 155 +++ .../skynetservices/skydns/server/server.go | 888 ++++++++++++++++++ .../skynetservices/skydns/server/stub.go | 124 +++ .../skydns/singleflight/singleflight.go | 64 ++ 21 files changed, 3129 insertions(+), 37 deletions(-) create mode 100644 vendor/github.com/coreos/go-systemd/activation/files.go create mode 100644 vendor/github.com/coreos/go-systemd/activation/listeners.go create mode 100644 vendor/github.com/coreos/go-systemd/activation/packetconns.go create mode 100644 vendor/github.com/skynetservices/skydns/cache/cache.go create mode 100644 vendor/github.com/skynetservices/skydns/cache/hit.go create mode 100644 vendor/github.com/skynetservices/skydns/metrics/metrics.go create mode 100644 vendor/github.com/skynetservices/skydns/server/backend.go create mode 100644 vendor/github.com/skynetservices/skydns/server/config.go create mode 100644 vendor/github.com/skynetservices/skydns/server/dnssec.go create mode 100644 vendor/github.com/skynetservices/skydns/server/doc.go create mode 100644 vendor/github.com/skynetservices/skydns/server/exchange.go create mode 100644 vendor/github.com/skynetservices/skydns/server/forwarding.go create mode 100644 vendor/github.com/skynetservices/skydns/server/log.go create mode 100644 vendor/github.com/skynetservices/skydns/server/msg.go create mode 100644 vendor/github.com/skynetservices/skydns/server/nsec3.go create mode 100644 vendor/github.com/skynetservices/skydns/server/server.go create mode 100644 vendor/github.com/skynetservices/skydns/server/stub.go create mode 100644 vendor/github.com/skynetservices/skydns/singleflight/singleflight.go diff --git a/Godeps/.license_file_state b/Godeps/.license_file_state index 11500124d6c..ebc33d5e3f7 100644 --- a/Godeps/.license_file_state +++ b/Godeps/.license_file_state @@ -71,6 +71,10 @@ raw.githubusercontent.com/dgrijalva/jwt-go/master/NOTICE raw.githubusercontent.com/dgrijalva/jwt-go/master/NOTICE.txt raw.githubusercontent.com/dgrijalva/jwt-go/master/README raw.githubusercontent.com/dgrijalva/jwt-go/master/README.md +raw.githubusercontent.com/docker/distribution/master/NOTICE +raw.githubusercontent.com/docker/distribution/master/NOTICE.txt +raw.githubusercontent.com/docker/distribution/master/README +raw.githubusercontent.com/docker/distribution/master/README.md raw.githubusercontent.com/docker/engine-api/master/NOTICE raw.githubusercontent.com/docker/engine-api/master/NOTICE.txt raw.githubusercontent.com/docker/engine-api/master/README @@ -129,6 +133,10 @@ raw.githubusercontent.com/golang/groupcache/master/NOTICE raw.githubusercontent.com/golang/groupcache/master/NOTICE.txt raw.githubusercontent.com/golang/groupcache/master/README raw.githubusercontent.com/golang/groupcache/master/README.md +raw.githubusercontent.com/golang/mock/master/NOTICE +raw.githubusercontent.com/golang/mock/master/NOTICE.txt +raw.githubusercontent.com/golang/mock/master/README +raw.githubusercontent.com/golang/mock/master/README.md raw.githubusercontent.com/golang/protobuf/master/NOTICE raw.githubusercontent.com/golang/protobuf/master/NOTICE.txt raw.githubusercontent.com/golang/protobuf/master/README @@ -247,7 +255,6 @@ raw.githubusercontent.com/pmezard/go-difflib/master/NOTICE raw.githubusercontent.com/pmezard/go-difflib/master/NOTICE.txt raw.githubusercontent.com/pmezard/go-difflib/master/README raw.githubusercontent.com/pmezard/go-difflib/master/README.md -raw.githubusercontent.com/prometheus/common/master/LICENSE raw.githubusercontent.com/prometheus/common/master/LICENSE.code raw.githubusercontent.com/prometheus/common/master/LICENSE.txt raw.githubusercontent.com/prometheus/common/master/LICENSE.md @@ -315,6 +322,10 @@ raw.githubusercontent.com/vishvananda/netlink/master/NOTICE raw.githubusercontent.com/vishvananda/netlink/master/NOTICE.txt raw.githubusercontent.com/vishvananda/netlink/master/README raw.githubusercontent.com/vishvananda/netlink/master/README.md +raw.githubusercontent.com/vmware/govmomi/master/NOTICE +raw.githubusercontent.com/vmware/govmomi/master/NOTICE.txt +raw.githubusercontent.com/vmware/govmomi/master/README +raw.githubusercontent.com/vmware/govmomi/master/README.md raw.githubusercontent.com/xiang90/probing/master/NOTICE raw.githubusercontent.com/xiang90/probing/master/NOTICE.txt raw.githubusercontent.com/xiang90/probing/master/README @@ -367,6 +378,10 @@ gopkg.in/gcfg.v1/master/NOTICE gopkg.in/gcfg.v1/master/NOTICE.txt gopkg.in/gcfg.v1/master/README gopkg.in/gcfg.v1/master/README.md +gopkg.in/inf.v0/master/NOTICE +gopkg.in/inf.v0/master/NOTICE.txt +gopkg.in/inf.v0/master/README +gopkg.in/inf.v0/master/README.md gopkg.in/natefinch/master/NOTICE gopkg.in/natefinch/master/NOTICE.txt gopkg.in/natefinch/master/README @@ -379,39 +394,3 @@ k8s.io/heapster/master/NOTICE k8s.io/heapster/master/NOTICE.txt k8s.io/heapster/master/README k8s.io/heapster/master/README.md -speter.net/go/master/NOTICE -speter.net/go/master/NOTICE.txt -speter.net/go/master/README -speter.net/go/master/README.md -gopkg.in/gcfg.v1/master/NOTICE -gopkg.in/gcfg.v1/master/NOTICE.txt -gopkg.in/gcfg.v1/master/README -gopkg.in/gcfg.v1/master/README.md -raw.githubusercontent.com/docker/engine-api/master/NOTICE -raw.githubusercontent.com/docker/engine-api/master/NOTICE.txt -raw.githubusercontent.com/docker/engine-api/master/README -raw.githubusercontent.com/docker/engine-api/master/README.md -raw.githubusercontent.com/docker/go-connections/master/NOTICE -raw.githubusercontent.com/docker/go-connections/master/NOTICE.txt -raw.githubusercontent.com/docker/go-connections/master/README -raw.githubusercontent.com/docker/go-connections/master/README.md -raw.githubusercontent.com/Microsoft/go-winio/master/NOTICE -raw.githubusercontent.com/Microsoft/go-winio/master/NOTICE.txt -raw.githubusercontent.com/Microsoft/go-winio/master/README -raw.githubusercontent.com/Microsoft/go-winio/master/README.md -raw.githubusercontent.com/docker/distribution/master/NOTICE -raw.githubusercontent.com/docker/distribution/master/NOTICE.txt -raw.githubusercontent.com/docker/distribution/master/README -raw.githubusercontent.com/docker/distribution/master/README.md -raw.githubusercontent.com/golang/mock/master/NOTICE -raw.githubusercontent.com/golang/mock/master/NOTICE.txt -raw.githubusercontent.com/golang/mock/master/README -raw.githubusercontent.com/golang/mock/master/README.md -raw.githubusercontent.com/vmware/govmomi/master/NOTICE -raw.githubusercontent.com/vmware/govmomi/master/NOTICE.txt -raw.githubusercontent.com/vmware/govmomi/master/README -raw.githubusercontent.com/vmware/govmomi/master/README.md -gopkg.in/inf.v0/master/NOTICE -gopkg.in/inf.v0/master/NOTICE.txt -gopkg.in/inf.v0/master/README -gopkg.in/inf.v0/master/README.md diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 92511653a1d..5efefcfbc2f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -507,6 +507,11 @@ "ImportPath": "github.com/coreos/go-semver/semver", "Rev": "d043ae190b3202550d026daf009359bb5d761672" }, + { + "ImportPath": "github.com/coreos/go-systemd/activation", + "Comment": "v4", + "Rev": "b4a58d95188dd092ae20072bac14cece0e67c388" + }, { "ImportPath": "github.com/coreos/go-systemd/daemon", "Comment": "v4", @@ -1777,11 +1782,31 @@ "ImportPath": "github.com/shurcooL/sanitized_anchor_name", "Rev": "9a8b7d4e8f347bfa230879db9d7d4e4d9e19f962" }, + { + "ImportPath": "github.com/skynetservices/skydns/cache", + "Comment": "2.5.3a-32-gf7b6fb7", + "Rev": "f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78" + }, + { + "ImportPath": "github.com/skynetservices/skydns/metrics", + "Comment": "2.5.3a-32-gf7b6fb7", + "Rev": "f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78" + }, { "ImportPath": "github.com/skynetservices/skydns/msg", "Comment": "2.5.3a-32-gf7b6fb7", "Rev": "f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78" }, + { + "ImportPath": "github.com/skynetservices/skydns/server", + "Comment": "2.5.3a-32-gf7b6fb7", + "Rev": "f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78" + }, + { + "ImportPath": "github.com/skynetservices/skydns/singleflight", + "Comment": "2.5.3a-32-gf7b6fb7", + "Rev": "f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78" + }, { "ImportPath": "github.com/spf13/cobra", "Rev": "4c05eb1145f16d0e6bb4a3e1b6d769f4713cb41f" diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 4a9be26cb77..9f8369c1f88 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -19925,6 +19925,202 @@ This product includes software developed at CoreOS, Inc. limitations under the License. +================================================================================ += vendor/github.com/coreos/go-systemd/activation licensed under: = + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + ================================================================================ = vendor/github.com/coreos/go-systemd/daemon licensed under: = @@ -53806,6 +54002,208 @@ SoundCloud Ltd. (http://soundcloud.com/). ================================================================================ = vendor/github.com/prometheus/common/expfmt licensed under: = + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + Copyright 2015 The Prometheus Authors This product includes software developed at @@ -53814,6 +54212,208 @@ SoundCloud Ltd. (http://soundcloud.com/). ================================================================================ = vendor/github.com/prometheus/common/model licensed under: = + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + Copyright 2015 The Prometheus Authors This product includes software developed at @@ -59266,6 +59866,58 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +================================================================================ += vendor/github.com/skynetservices/skydns/cache licensed under: = + +The MIT License (MIT) + +Copyright (c) 2013 The SkyDNS Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +================================================================================ += vendor/github.com/skynetservices/skydns/metrics licensed under: = + +The MIT License (MIT) + +Copyright (c) 2013 The SkyDNS Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ================================================================================ = vendor/github.com/skynetservices/skydns/msg licensed under: = @@ -59292,6 +59944,58 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +================================================================================ += vendor/github.com/skynetservices/skydns/server licensed under: = + +The MIT License (MIT) + +Copyright (c) 2013 The SkyDNS Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +================================================================================ += vendor/github.com/skynetservices/skydns/singleflight licensed under: = + +The MIT License (MIT) + +Copyright (c) 2013 The SkyDNS Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ================================================================================ = vendor/github.com/spf13/cobra licensed under: = diff --git a/vendor/github.com/coreos/go-systemd/activation/files.go b/vendor/github.com/coreos/go-systemd/activation/files.go new file mode 100644 index 00000000000..c8e85fcd588 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/activation/files.go @@ -0,0 +1,52 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package activation implements primitives for systemd socket activation. +package activation + +import ( + "os" + "strconv" + "syscall" +) + +// based on: https://gist.github.com/alberts/4640792 +const ( + listenFdsStart = 3 +) + +func Files(unsetEnv bool) []*os.File { + if unsetEnv { + defer os.Unsetenv("LISTEN_PID") + defer os.Unsetenv("LISTEN_FDS") + } + + pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) + if err != nil || pid != os.Getpid() { + return nil + } + + nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) + if err != nil || nfds == 0 { + return nil + } + + files := make([]*os.File, 0, nfds) + for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { + syscall.CloseOnExec(fd) + files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))) + } + + return files +} diff --git a/vendor/github.com/coreos/go-systemd/activation/listeners.go b/vendor/github.com/coreos/go-systemd/activation/listeners.go new file mode 100644 index 00000000000..df27c29e9ea --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/activation/listeners.go @@ -0,0 +1,62 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package activation + +import ( + "crypto/tls" + "net" +) + +// Listeners returns a slice containing a net.Listener for each matching socket type +// passed to this process. +// +// The order of the file descriptors is preserved in the returned slice. +// Nil values are used to fill any gaps. For example if systemd were to return file descriptors +// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} +func Listeners(unsetEnv bool) ([]net.Listener, error) { + files := Files(unsetEnv) + listeners := make([]net.Listener, len(files)) + + for i, f := range files { + if pc, err := net.FileListener(f); err == nil { + listeners[i] = pc + } + } + return listeners, nil +} + +// TLSListeners returns a slice containing a net.listener for each matching TCP socket type +// passed to this process. +// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. +func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) { + listeners, err := Listeners(unsetEnv) + + if listeners == nil || err != nil { + return nil, err + } + + if tlsConfig != nil && err == nil { + tlsConfig.NextProtos = []string{"http/1.1"} + + for i, l := range listeners { + // Activate TLS only for TCP sockets + if l.Addr().Network() == "tcp" { + listeners[i] = tls.NewListener(l, tlsConfig) + } + } + } + + return listeners, err +} diff --git a/vendor/github.com/coreos/go-systemd/activation/packetconns.go b/vendor/github.com/coreos/go-systemd/activation/packetconns.go new file mode 100644 index 00000000000..48b2ca029df --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/activation/packetconns.go @@ -0,0 +1,37 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package activation + +import ( + "net" +) + +// PacketConns returns a slice containing a net.PacketConn for each matching socket type +// passed to this process. +// +// The order of the file descriptors is preserved in the returned slice. +// Nil values are used to fill any gaps. For example if systemd were to return file descriptors +// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} +func PacketConns(unsetEnv bool) ([]net.PacketConn, error) { + files := Files(unsetEnv) + conns := make([]net.PacketConn, len(files)) + + for i, f := range files { + if pc, err := net.FilePacketConn(f); err == nil { + conns[i] = pc + } + } + return conns, nil +} diff --git a/vendor/github.com/skynetservices/skydns/cache/cache.go b/vendor/github.com/skynetservices/skydns/cache/cache.go new file mode 100644 index 00000000000..60e42cd5017 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/cache/cache.go @@ -0,0 +1,167 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package cache + +// Cache that holds RRs and for DNSSEC an RRSIG. + +// TODO(miek): there is a lot of copying going on to copy myself out of data +// races. This should be optimized. + +import ( + "crypto/sha1" + "sync" + "time" + + "github.com/miekg/dns" +) + +// Elem hold an answer and additional section that returned from the cache. +// The signature is put in answer, extra is empty there. This wastes some memory. +type elem struct { + expiration time.Time // time added + TTL, after this the elem is invalid + msg *dns.Msg +} + +// Cache is a cache that holds on the a number of RRs or DNS messages. The cache +// eviction is randomized. +type Cache struct { + sync.RWMutex + + capacity int + m map[string]*elem + ttl time.Duration +} + +// New returns a new cache with the capacity and the ttl specified. +func New(capacity, ttl int) *Cache { + c := new(Cache) + c.m = make(map[string]*elem) + c.capacity = capacity + c.ttl = time.Duration(ttl) * time.Second + return c +} + +func (c *Cache) Capacity() int { return c.capacity } + +func (c *Cache) Remove(s string) { + c.Lock() + delete(c.m, s) + c.Unlock() +} + +// EvictRandom removes a random member a the cache. +// Must be called under a write lock. +func (c *Cache) EvictRandom() { + clen := len(c.m) + if clen < c.capacity { + return + } + i := c.capacity - clen + for k, _ := range c.m { + delete(c.m, k) + i-- + if i == 0 { + break + } + } +} + +// InsertMessage inserts a message in the Cache. We will cache it for ttl seconds, which +// should be a small (60...300) integer. +func (c *Cache) InsertMessage(s string, msg *dns.Msg) { + if c.capacity <= 0 { + return + } + + c.Lock() + if _, ok := c.m[s]; !ok { + c.m[s] = &elem{time.Now().UTC().Add(c.ttl), msg.Copy()} + + } + c.EvictRandom() + c.Unlock() +} + +// InsertSignature inserts a signature, the expiration time is used as the cache ttl. +func (c *Cache) InsertSignature(s string, sig *dns.RRSIG) { + if c.capacity <= 0 { + return + } + c.Lock() + + if _, ok := c.m[s]; !ok { + m := ((int64(sig.Expiration) - time.Now().Unix()) / (1 << 31)) - 1 + if m < 0 { + m = 0 + } + t := time.Unix(int64(sig.Expiration)-(m*(1<<31)), 0).UTC() + c.m[s] = &elem{t, &dns.Msg{Answer: []dns.RR{dns.Copy(sig)}}} + } + c.EvictRandom() + c.Unlock() +} + +// Search returns a dns.Msg, the expiration time and a boolean indicating if we found something +// in the cache. +func (c *Cache) Search(s string) (*dns.Msg, time.Time, bool) { + if c.capacity <= 0 { + return nil, time.Time{}, false + } + c.RLock() + if e, ok := c.m[s]; ok { + e1 := e.msg.Copy() + c.RUnlock() + return e1, e.expiration, true + } + c.RUnlock() + return nil, time.Time{}, false +} + +// Key creates a hash key from a question section. It creates a different key +// for requests with DNSSEC. +func Key(q dns.Question, dnssec, tcp bool) string { + h := sha1.New() + i := append([]byte(q.Name), packUint16(q.Qtype)...) + if dnssec { + i = append(i, byte(255)) + } + if tcp { + i = append(i, byte(254)) + } + return string(h.Sum(i)) +} + +// Key uses the name, type and rdata, which is serialized and then hashed as the key for the lookup. +func KeyRRset(rrs []dns.RR) string { + h := sha1.New() + i := []byte(rrs[0].Header().Name) + i = append(i, packUint16(rrs[0].Header().Rrtype)...) + for _, r := range rrs { + switch t := r.(type) { // we only do a few type, serialize these manually + case *dns.SOA: + // We only fiddle with the serial so store that. + i = append(i, packUint32(t.Serial)...) + case *dns.SRV: + i = append(i, packUint16(t.Priority)...) + i = append(i, packUint16(t.Weight)...) + i = append(i, packUint16(t.Weight)...) + i = append(i, []byte(t.Target)...) + case *dns.A: + i = append(i, []byte(t.A)...) + case *dns.AAAA: + i = append(i, []byte(t.AAAA)...) + case *dns.NSEC3: + i = append(i, []byte(t.NextDomain)...) + // Bitmap does not differentiate in SkyDNS. + case *dns.DNSKEY: + case *dns.NS: + case *dns.TXT: + } + } + return string(h.Sum(i)) +} + +func packUint16(i uint16) []byte { return []byte{byte(i >> 8), byte(i)} } +func packUint32(i uint32) []byte { return []byte{byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i)} } diff --git a/vendor/github.com/skynetservices/skydns/cache/hit.go b/vendor/github.com/skynetservices/skydns/cache/hit.go new file mode 100644 index 00000000000..2b0a5614cbd --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/cache/hit.go @@ -0,0 +1,31 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package cache + +import ( + "time" + + "github.com/miekg/dns" +) + +// Hit returns a dns message from the cache. If the message's TTL is expired nil +// is returned and the message is removed from the cache. +func (c *Cache) Hit(question dns.Question, dnssec, tcp bool, msgid uint16) *dns.Msg { + key := Key(question, dnssec, tcp) + m1, exp, hit := c.Search(key) + if hit { + // Cache hit! \o/ + if time.Since(exp) < 0 { + m1.Id = msgid + m1.Compress = true + // Even if something ended up with the TC bit *in* the cache, set it to off + m1.Truncated = false + return m1 + } + // Expired! /o\ + c.Remove(key) + } + return nil +} diff --git a/vendor/github.com/skynetservices/skydns/metrics/metrics.go b/vendor/github.com/skynetservices/skydns/metrics/metrics.go new file mode 100644 index 00000000000..7534b756bc5 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/metrics/metrics.go @@ -0,0 +1,186 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package metrics + +import ( + "fmt" + "net/http" + "os" + "strconv" + "time" + + "github.com/miekg/dns" + "github.com/prometheus/client_golang/prometheus" +) + +var ( + Port = os.Getenv("PROMETHEUS_PORT") + Path = envOrDefault("PROMETHEUS_PATH", "/metrics") + Namespace = envOrDefault("PROMETHEUS_NAMESPACE", "skydns") + Subsystem = envOrDefault("PROMETHEUS_SUBSYSTEM", "skydns") + + requestCount *prometheus.CounterVec + requestDuration *prometheus.HistogramVec + responseSize *prometheus.HistogramVec + errorCount *prometheus.CounterVec + cacheMiss *prometheus.CounterVec +) + +type ( + System string + Cause string + CacheType string +) + +var ( + Auth System = "auth" + Cache System = "cache" + Rec System = "recursive" + Reverse System = "reverse" + Stub System = "stub" + + Nxdomain Cause = "nxdomain" + Nodata Cause = "nodata" + Truncated Cause = "truncated" + Refused Cause = "refused" + Overflow Cause = "overflow" + Fail Cause = "servfail" + + Response CacheType = "response" + Signature CacheType = "signature" +) + +func defineMetrics() { + requestCount = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "dns_request_count_total", + Help: "Counter of DNS requests made.", + }, []string{"system"}) + + requestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "dns_request_duration_seconds", + Help: "Histogram of the time (in seconds) each request took to resolve.", + Buckets: append([]float64{0.001, 0.003}, prometheus.DefBuckets...), + }, []string{"system"}) + + responseSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "dns_response_size_bytes", + Help: "Size of the returns response in bytes.", + Buckets: []float64{0, 512, 1024, 1500, 2048, 4096, + 8192, 12288, 16384, 20480, 24576, 28672, 32768, 36864, + 40960, 45056, 49152, 53248, 57344, 61440, 65536, + }, + }, []string{"system"}) + + errorCount = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "dns_error_count_total", + Help: "Counter of DNS requests resulting in an error.", + }, []string{"system", "cause"}) + + cacheMiss = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: Namespace, + Subsystem: Subsystem, + Name: "dns_cachemiss_count_total", + Help: "Counter of DNS requests that result in a cache miss.", + }, []string{"cache"}) +} + +// Metrics registers the DNS metrics to Prometheus, and starts the internal metrics +// server if the environment variable PROMETHEUS_PORT is set. +func Metrics() error { + // We do this in a function instead of using var + init(), because we want to + // able to set Namespace and/or Subsystem. + if Port == "" { + return nil + } + + _, err := strconv.Atoi(Port) + if err != nil { + fmt.Errorf("bad port for prometheus: %s", Port) + } + + defineMetrics() + + prometheus.MustRegister(requestCount) + prometheus.MustRegister(requestDuration) + prometheus.MustRegister(responseSize) + prometheus.MustRegister(errorCount) + prometheus.MustRegister(cacheMiss) + + http.Handle(Path, prometheus.Handler()) + go func() { + fmt.Errorf("%s", http.ListenAndServe(":"+Port, nil)) + }() + return nil +} + +func ReportDuration(resp *dns.Msg, start time.Time, sys System) { + if requestDuration == nil || responseSize == nil { + return + } + + rlen := float64(0) + if resp != nil { + rlen = float64(resp.Len()) + } + requestDuration.WithLabelValues(string(sys)).Observe(float64(time.Since(start)) / float64(time.Second)) + responseSize.WithLabelValues(string(sys)).Observe(rlen) +} + +func ReportRequestCount(req *dns.Msg, sys System) { + if requestCount == nil { + return + } + + requestCount.WithLabelValues(string(sys)).Inc() +} + +func ReportErrorCount(resp *dns.Msg, sys System) { + if resp == nil || errorCount == nil { + return + } + + if resp.Truncated { + errorCount.WithLabelValues(string(sys), string(Truncated)).Inc() + return + } + if resp.Len() > dns.MaxMsgSize { + errorCount.WithLabelValues(string(sys), string(Overflow)).Inc() + return + } + + switch resp.Rcode { + case dns.RcodeServerFailure: + errorCount.WithLabelValues(string(sys), string(Fail)).Inc() + case dns.RcodeRefused: + errorCount.WithLabelValues(string(sys), string(Refused)).Inc() + case dns.RcodeNameError: + errorCount.WithLabelValues(string(sys), string(Nxdomain)).Inc() + // nodata ?? + } + +} + +func ReportCacheMiss(ca CacheType) { + if cacheMiss == nil { + return + } + cacheMiss.WithLabelValues(string(ca)).Inc() +} + +func envOrDefault(env, def string) string { + e := os.Getenv(env) + if e != "" { + return e + } + return def +} diff --git a/vendor/github.com/skynetservices/skydns/server/backend.go b/vendor/github.com/skynetservices/skydns/server/backend.go new file mode 100644 index 00000000000..bb3a8cdd794 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/backend.go @@ -0,0 +1,46 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import "github.com/skynetservices/skydns/msg" + +type Backend interface { + Records(name string, exact bool) ([]msg.Service, error) + ReverseRecord(name string) (*msg.Service, error) +} + +// FirstBackend exposes the Backend interface over multiple Backends, returning +// the first Backend that answers the provided record request. If no Backend answers +// a record request, the last error seen will be returned. +type FirstBackend []Backend + +// FirstBackend implements Backend +var _ Backend = FirstBackend{} + +func (g FirstBackend) Records(name string, exact bool) (records []msg.Service, err error) { + var lastError error + for _, backend := range g { + if records, err = backend.Records(name, exact); err == nil && len(records) > 0 { + return records, nil + } + if err != nil { + lastError = err + } + } + return nil, lastError +} + +func (g FirstBackend) ReverseRecord(name string) (record *msg.Service, err error) { + var lastError error + for _, backend := range g { + if record, err = backend.ReverseRecord(name); err == nil && record != nil { + return record, nil + } + if err != nil { + lastError = err + } + } + return nil, lastError +} diff --git a/vendor/github.com/skynetservices/skydns/server/config.go b/vendor/github.com/skynetservices/skydns/server/config.go new file mode 100644 index 00000000000..3e4c6cfb078 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/config.go @@ -0,0 +1,157 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import ( + "crypto" + "fmt" + "net" + "os" + "strings" + "time" + + "github.com/miekg/dns" +) + +const ( + SCacheCapacity = 10000 + RCacheCapacity = 100000 + RCacheTtl = 60 +) + +// Config provides options to the SkyDNS resolver. +type Config struct { + // The ip:port SkyDNS should be listening on for incoming DNS requests. + DnsAddr string `json:"dns_addr,omitempty"` + // bind to port(s) activated by systemd. If set to true, this overrides DnsAddr. + Systemd bool `json:"systemd,omitempty"` + // The domain SkyDNS is authoritative for, defaults to skydns.local. + Domain string `json:"domain,omitempty"` + // Domain pointing to a key where service info is stored when being queried + // for local.dns.skydns.local. + Local string `json:"local,omitempty"` + // The hostmaster responsible for this domain, defaults to hostmaster.. + Hostmaster string `json:"hostmaster,omitempty"` + DNSSEC string `json:"dnssec,omitempty"` + // Round robin A/AAAA replies. Default is true. + RoundRobin bool `json:"round_robin,omitempty"` + // Round robin selection of nameservers from among those listed, rather than have all forwarded requests try the first listed server first every time. + NSRotate bool `json:"ns_rotate,omitempty"` + // List of ip:port, seperated by commas of recursive nameservers to forward queries to. + Nameservers []string `json:"nameservers,omitempty"` + // Never provide a recursive service. + NoRec bool `json:"no_rec,omitempty"` + ReadTimeout time.Duration `json:"read_timeout,omitempty"` + // Default priority on SRV records when none is given. Defaults to 10. + Priority uint16 `json:"priority"` + // Default TTL, in seconds, when none is given in etcd. Defaults to 3600. + Ttl uint32 `json:"ttl,omitempty"` + // Minimum TTL, in seconds, for NXDOMAIN responses. Defaults to 300. + MinTtl uint32 `json:"min_ttl,omitempty"` + // SCache, capacity of the signature cache in signatures stored. + SCache int `json:"scache,omitempty"` + // RCache, capacity of response cache in resource records stored. + RCache int `json:"rcache,omitempty"` + // RCacheTtl, how long to cache in seconds. + RCacheTtl int `json:"rcache_ttl,omitempty"` + // How many labels a name should have before we allow forwarding. Default to 2. + Ndots int `json:"ndot,omitempty"` + + // DNSSEC key material + PubKey *dns.DNSKEY `json:"-"` + KeyTag uint16 `json:"-"` + PrivKey crypto.Signer `json:"-"` + + Verbose bool `json:"-"` + + Version bool + + // some predefined string "constants" + localDomain string // "local.dns." + config.Domain + dnsDomain string // "ns.dns". + config.Domain + + // Stub zones support. Pointer to a map that we refresh when we see + // an update. Map contains domainname -> nameserver:port + stub *map[string][]string +} + +func SetDefaults(config *Config) error { + if config.ReadTimeout == 0 { + config.ReadTimeout = 2 * time.Second + } + if config.DnsAddr == "" { + config.DnsAddr = "127.0.0.1:53" + } + if config.Domain == "" { + config.Domain = "skydns.local." + } + if config.Hostmaster == "" { + config.Hostmaster = appendDomain("hostmaster", config.Domain) + } + // People probably don't know that SOA's email addresses cannot + // contain @-signs, replace them with dots + config.Hostmaster = dns.Fqdn(strings.Replace(config.Hostmaster, "@", ".", -1)) + if config.MinTtl == 0 { + config.MinTtl = 60 + } + if config.Ttl == 0 { + config.Ttl = 3600 + } + if config.Priority == 0 { + config.Priority = 10 + } + if config.RCache < 0 { + config.RCache = 0 + } + if config.SCache < 0 { + config.SCache = 0 + } + if config.RCacheTtl == 0 { + config.RCacheTtl = RCacheTtl + } + if config.Ndots <= 0 { + config.Ndots = 2 + } + + if len(config.Nameservers) == 0 { + c, err := dns.ClientConfigFromFile("/etc/resolv.conf") + if !os.IsNotExist(err) { + if err != nil { + return err + } + for _, s := range c.Servers { + config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port)) + } + } + } + config.Domain = dns.Fqdn(strings.ToLower(config.Domain)) + if config.DNSSEC != "" { + // For some reason the + are replaces by spaces in etcd. Re-replace them + keyfile := strings.Replace(config.DNSSEC, " ", "+", -1) + k, p, err := ParseKeyFile(keyfile) + if err != nil { + return err + } + if k.Header().Name != dns.Fqdn(config.Domain) { + return fmt.Errorf("ownername of DNSKEY must match SkyDNS domain") + } + k.Header().Ttl = config.Ttl + config.PubKey = k + config.KeyTag = k.KeyTag() + config.PrivKey = p + } + config.localDomain = appendDomain("local.dns", config.Domain) + config.dnsDomain = appendDomain("ns.dns", config.Domain) + stubmap := make(map[string][]string) + config.stub = &stubmap + return nil +} + +func appendDomain(s1, s2 string) string { + if len(s2) > 0 && s2[0] == '.' { + return s1 + s2 + } + return s1 + "." + s2 +} diff --git a/vendor/github.com/skynetservices/skydns/server/dnssec.go b/vendor/github.com/skynetservices/skydns/server/dnssec.go new file mode 100644 index 00000000000..587d054acfb --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/dnssec.go @@ -0,0 +1,177 @@ +// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "os" + "time" + + "github.com/skynetservices/skydns/cache" + "github.com/skynetservices/skydns/metrics" + "github.com/skynetservices/skydns/singleflight" + + "github.com/miekg/dns" +) +var ( + inflight = &singleflight.Group{} +) + + +// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other +// utilities. It add ".key" for the public key and ".private" for the private key. +func ParseKeyFile(file string) (*dns.DNSKEY, crypto.Signer, error) { + f, e := os.Open(file + ".key") + if e != nil { + return nil, nil, e + } + k, e := dns.ReadRR(f, file+".key") + if e != nil { + return nil, nil, e + } + f, e = os.Open(file + ".private") + if e != nil { + return nil, nil, e + } + p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, file+".private") + if e != nil { + return nil, nil, e + } + + if v, ok := p.(*rsa.PrivateKey); ok { + return k.(*dns.DNSKEY), v, nil + } + if v, ok := p.(*ecdsa.PrivateKey); ok { + return k.(*dns.DNSKEY), v, nil + } + return k.(*dns.DNSKEY), nil, nil +} + +// Sign signs a message m, it takes care of negative or nodata responses as +// well by synthesising NSEC3 records. It will also cache the signatures, using +// a hash of the signed data as a key. +// We also fake the origin TTL in the signature, because we don't want to +// throw away signatures when services decide to have longer TTL. So we just +// set the origTTL to 60. +// TODO(miek): revisit origTTL +func (s *server) Sign(m *dns.Msg, bufsize uint16) { + now := time.Now().UTC() + incep := uint32(now.Add(-3 * time.Hour).Unix()) // 2+1 hours, be sure to catch daylight saving time and such + expir := uint32(now.Add(7 * 24 * time.Hour).Unix()) // sign for a week + + for _, r := range rrSets(m.Answer) { + if r[0].Header().Rrtype == dns.TypeRRSIG { + continue + } + if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) { + continue + } + if sig, err := s.signSet(r, now, incep, expir); err == nil { + m.Answer = append(m.Answer, sig) + } + } + for _, r := range rrSets(m.Ns) { + if r[0].Header().Rrtype == dns.TypeRRSIG { + continue + } + if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) { + continue + } + if sig, err := s.signSet(r, now, incep, expir); err == nil { + m.Ns = append(m.Ns, sig) + } + } + for _, r := range rrSets(m.Extra) { + if r[0].Header().Rrtype == dns.TypeRRSIG || r[0].Header().Rrtype == dns.TypeOPT { + continue + } + if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) { + continue + } + if sig, err := s.signSet(r, now, incep, expir); err == nil { + m.Extra = append(m.Extra, sig) + } + } + + o := new(dns.OPT) + o.Hdr.Name = "." + o.Hdr.Rrtype = dns.TypeOPT + o.SetDo() + o.SetUDPSize(4096) // TODO(miek): echo client + m.Extra = append(m.Extra, o) + return +} + +func (s *server) signSet(r []dns.RR, now time.Time, incep, expir uint32) (*dns.RRSIG, error) { + key := cache.KeyRRset(r) + if m, exp, hit := s.scache.Search(key); hit { // There can only be one sig in this cache. + // Is it still valid 24 hours from now? + if now.Add(+24*time.Hour).Sub(exp) < -24*time.Hour { + return m.Answer[0].(*dns.RRSIG), nil + } + s.scache.Remove(key) + } + if s.config.Verbose { + logf("scache miss for %s type %d", r[0].Header().Name, r[0].Header().Rrtype) + } + + metrics.ReportCacheMiss("signature") + + sig, err := inflight.Do(key, func() (interface{}, error) { + sig1 := s.NewRRSIG(incep, expir) + sig1.Header().Ttl = r[0].Header().Ttl + if r[0].Header().Rrtype == dns.TypeTXT { + sig1.OrigTtl = 0 + } + e := sig1.Sign(s.config.PrivKey, r) + if e != nil { + logf("failed to sign: %s", e.Error()) + } + return sig1, e + }) + if err != nil { + return nil, err + } + s.scache.InsertSignature(key, sig.(*dns.RRSIG)) + return dns.Copy(sig.(*dns.RRSIG)).(*dns.RRSIG), nil +} + +func (s *server) NewRRSIG(incep, expir uint32) *dns.RRSIG { + sig := new(dns.RRSIG) + sig.Hdr.Rrtype = dns.TypeRRSIG + sig.Hdr.Ttl = s.config.Ttl + sig.OrigTtl = s.config.Ttl + sig.Algorithm = s.config.PubKey.Algorithm + sig.KeyTag = s.config.KeyTag + sig.Inception = incep + sig.Expiration = expir + sig.SignerName = s.config.PubKey.Hdr.Name + return sig +} + +type rrset struct { + qname string + qtype uint16 +} + +func rrSets(rrs []dns.RR) map[rrset][]dns.RR { + m := make(map[rrset][]dns.RR) + for _, r := range rrs { + if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok { + s = append(s, r) + m[rrset{r.Header().Name, r.Header().Rrtype}] = s + } else { + s := make([]dns.RR, 1, 3) + s[0] = r + m[rrset{r.Header().Name, r.Header().Rrtype}] = s + } + } + if len(m) > 0 { + return m + } + return nil +} diff --git a/vendor/github.com/skynetservices/skydns/server/doc.go b/vendor/github.com/skynetservices/skydns/server/doc.go new file mode 100644 index 00000000000..d2009a65141 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/doc.go @@ -0,0 +1,8 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +// Package server provides a DNS server implementation that handles DNS +// queries. To answer a query, the server asks the provided Backend for +// DNS records, which are then converted to the proper answers. +package server diff --git a/vendor/github.com/skynetservices/skydns/server/exchange.go b/vendor/github.com/skynetservices/skydns/server/exchange.go new file mode 100644 index 00000000000..2fb5be9ace0 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/exchange.go @@ -0,0 +1,34 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import "github.com/miekg/dns" + +// exchangeMsg returns a new dns message based on name, type, bufsize and dnssec. +func newExchangeMsg(name string, typ, bufsize uint16, dnssec bool) *dns.Msg { + m := new(dns.Msg) + m.SetQuestion(name, typ) + m.SetEdns0(bufsize, dnssec) + return m +} + +// exchangeWithRetry sends message m to server, but retries on ServerFailure. +func exchangeWithRetry(c *dns.Client, m *dns.Msg, server string) (*dns.Msg, error) { + r, _, err := c.Exchange(m, server) + if err == nil && r.Rcode == dns.RcodeServerFailure { + // redo the query + r, _, err = c.Exchange(m, server) + } + return r, err +} + +func (s *server) randomNameserverID(id uint16) int { + nsid := 0 + if s.config.NSRotate { + // Use request Id for "random" nameserver selection. + nsid = int(id) % len(s.config.Nameservers) + } + return nsid +} diff --git a/vendor/github.com/skynetservices/skydns/server/forwarding.go b/vendor/github.com/skynetservices/skydns/server/forwarding.go new file mode 100644 index 00000000000..a3c8cb32db8 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/forwarding.go @@ -0,0 +1,125 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import ( + "fmt" + + "github.com/miekg/dns" +) + +// ServeDNSForward forwards a request to a nameservers and returns the response. +func (s *server) ServeDNSForward(w dns.ResponseWriter, req *dns.Msg) *dns.Msg { + if s.config.NoRec { + m := s.ServerFailure(req) + w.WriteMsg(m) + return m + } + + if len(s.config.Nameservers) == 0 || dns.CountLabel(req.Question[0].Name) < s.config.Ndots { + if s.config.Verbose { + if len(s.config.Nameservers) == 0 { + logf("can not forward, no nameservers defined") + } else { + logf("can not forward, name too short (less than %d labels): `%s'", s.config.Ndots, req.Question[0].Name) + } + } + m := s.ServerFailure(req) + m.RecursionAvailable = true // this is still true + w.WriteMsg(m) + return m + } + + var ( + r *dns.Msg + err error + ) + + nsid := s.randomNameserverID(req.Id) + try := 0 +Redo: + if isTCP(w) { + r, err = exchangeWithRetry(s.dnsTCPclient, req, s.config.Nameservers[nsid]) + } else { + r, err = exchangeWithRetry(s.dnsUDPclient, req, s.config.Nameservers[nsid]) + } + if err == nil { + r.Compress = true + r.Id = req.Id + w.WriteMsg(r) + return r + } + // Seen an error, this can only mean, "server not reached", try again + // but only if we have not exausted our nameservers. + if try < len(s.config.Nameservers) { + try++ + nsid = (nsid + 1) % len(s.config.Nameservers) + goto Redo + } + + logf("failure to forward request %q", err) + m := s.ServerFailure(req) + return m +} + +// ServeDNSReverse is the handler for DNS requests for the reverse zone. If nothing is found +// locally the request is forwarded to the forwarder for resolution. +func (s *server) ServeDNSReverse(w dns.ResponseWriter, req *dns.Msg) *dns.Msg { + m := new(dns.Msg) + m.SetReply(req) + m.Compress = true + m.Authoritative = false // Set to false, because I don't know what to do wrt DNSSEC. + m.RecursionAvailable = true + var err error + if m.Answer, err = s.PTRRecords(req.Question[0]); err == nil { + // TODO(miek): Reverse DNSSEC. We should sign this, but requires a key....and more + // Probably not worth the hassle? + if err := w.WriteMsg(m); err != nil { + logf("failure to return reply %q", err) + } + return m + } + // Always forward if not found locally. + return s.ServeDNSForward(w, req) +} + +// Lookup looks up name,type using the recursive nameserver defines +// in the server's config. If none defined it returns an error. +func (s *server) Lookup(n string, t, bufsize uint16, dnssec bool) (*dns.Msg, error) { + if len(s.config.Nameservers) == 0 { + return nil, fmt.Errorf("no nameservers configured can not lookup name") + } + if dns.CountLabel(n) < s.config.Ndots { + return nil, fmt.Errorf("name has fewer than %d labels", s.config.Ndots) + } + m := newExchangeMsg(n, t, bufsize, dnssec) + + nsid := s.randomNameserverID(m.Id) + try := 0 +Redo: + r, err := exchangeWithRetry(s.dnsUDPclient, m, s.config.Nameservers[nsid]) + if err == nil { + if r.Rcode != dns.RcodeSuccess { + return nil, fmt.Errorf("rcode is not equal to success") + } + // Reset TTLs to rcache TTL to make some of the other code + // and the tests not care about TTLs + for _, rr := range r.Answer { + rr.Header().Ttl = uint32(s.config.RCacheTtl) + } + for _, rr := range r.Extra { + rr.Header().Ttl = uint32(s.config.RCacheTtl) + } + return r, nil + } + // Seen an error, this can only mean, "server not reached", try again + // but only if we have not exausted our nameservers. + if try < len(s.config.Nameservers) { + try++ + nsid = (nsid + 1) % len(s.config.Nameservers) + goto Redo + } + return nil, fmt.Errorf("failure to lookup name") +} diff --git a/vendor/github.com/skynetservices/skydns/server/log.go b/vendor/github.com/skynetservices/skydns/server/log.go new file mode 100644 index 00000000000..13288f83822 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/log.go @@ -0,0 +1,17 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import "log" + +// printf calls log.Printf with the parameters given. +func logf(format string, a ...interface{}) { + log.Printf("skydns: "+format, a...) +} + +// fatalf calls log.Fatalf with the parameters given. +func fatalf(format string, a ...interface{}) { + log.Fatalf("skydns: "+format, a...) +} diff --git a/vendor/github.com/skynetservices/skydns/server/msg.go b/vendor/github.com/skynetservices/skydns/server/msg.go new file mode 100644 index 00000000000..6250ec8ad84 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/msg.go @@ -0,0 +1,54 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import "github.com/miekg/dns" + +// Fit will make m fit the size. If a message is larger than size then entire +// additional section is dropped. If it is still to large and the transport +// is udp we return a truncated message. +// If the transport is tcp we are going to drop RR from the answer section +// until it fits. When this is case the returned bool is true. +func Fit(m *dns.Msg, size int, tcp bool) (*dns.Msg, bool) { + if m.Len() > size { + // Check for OPT Records at the end and keep those. TODO(miek) + m.Extra = nil + } + if m.Len() < size { + return m, false + } + + // With TCP setting TC does not mean anything. + if !tcp { + m.Truncated = true + // fall through here, so we at least return a message that can + // fit the udp buffer. + } + + // Additional section is gone, binary search until we have length that fits. + min, max := 0, len(m.Answer) + original := make([]dns.RR, len(m.Answer)) + copy(original, m.Answer) + for { + if min == max { + break + } + + mid := (min + max) / 2 + m.Answer = original[:mid] + + if m.Len() < size { + min++ + continue + } + max = mid + + } + if max > 1 { + max-- + } + m.Answer = m.Answer[:max] + return m, true +} diff --git a/vendor/github.com/skynetservices/skydns/server/nsec3.go b/vendor/github.com/skynetservices/skydns/server/nsec3.go new file mode 100644 index 00000000000..bf911534c35 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/nsec3.go @@ -0,0 +1,155 @@ +// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import ( + "encoding/base32" + "strings" + + "github.com/miekg/dns" +) + +// Do DNSSEC NXDOMAIN with NSEC3 whitelies: rfc 7129, appendix B. +// The closest encloser will be qname - the left most label and the +// next closer will be the full qname which we then will deny. +// Idem for source of synthesis. + +func (s *server) Denial(m *dns.Msg) { + if m.Rcode == dns.RcodeNameError { + // ce is qname minus the left label + idx := dns.Split(m.Question[0].Name) + ce := m.Question[0].Name[idx[1]:] + + nsec3ce, nsec3wildcard := newNSEC3CEandWildcard(s.config.Domain, ce, s.config.MinTtl) + // Add ce and wildcard + m.Ns = append(m.Ns, nsec3ce) + m.Ns = append(m.Ns, nsec3wildcard) + // Deny Qname nsec3 + m.Ns = append(m.Ns, s.newNSEC3NameError(m.Question[0].Name)) + } + if m.Rcode == dns.RcodeSuccess && len(m.Ns) == 1 { + // NODATA + if _, ok := m.Ns[0].(*dns.SOA); ok { + m.Ns = append(m.Ns, s.newNSEC3NoData(m.Question[0].Name)) + } + } +} + +func packBase32(s string) []byte { + b32len := base32.HexEncoding.DecodedLen(len(s)) + buf := make([]byte, b32len) + n, _ := base32.HexEncoding.Decode(buf, []byte(s)) + buf = buf[:n] + return buf +} + +func unpackBase32(b []byte) string { + b32 := make([]byte, base32.HexEncoding.EncodedLen(len(b))) + base32.HexEncoding.Encode(b32, b) + return string(b32) +} + +// newNSEC3NameError returns the NSEC3 record needed to denial qname. +func (s *server) newNSEC3NameError(qname string) *dns.NSEC3 { + n := new(dns.NSEC3) + n.Hdr.Class = dns.ClassINET + n.Hdr.Rrtype = dns.TypeNSEC3 + n.Hdr.Ttl = s.config.MinTtl + n.Hash = dns.SHA1 + n.Flags = 0 + n.Salt = "" + n.TypeBitMap = []uint16{} + + covername := dns.HashName(qname, dns.SHA1, 0, "") + + buf := packBase32(covername) + byteArith(buf, false) // one before + n.Hdr.Name = appendDomain(strings.ToLower(unpackBase32(buf)), s.config.Domain) + byteArith(buf, true) // one next + byteArith(buf, true) // and another one + n.NextDomain = unpackBase32(buf) + return n +} + +// newNSEC3NoData returns the NSEC3 record needed to denial the types +func (s *server) newNSEC3NoData(qname string) *dns.NSEC3 { + n := new(dns.NSEC3) + n.Hdr.Class = dns.ClassINET + n.Hdr.Rrtype = dns.TypeNSEC3 + n.Hdr.Ttl = s.config.MinTtl + n.Hash = dns.SHA1 + n.Flags = 0 + n.Salt = "" + n.TypeBitMap = []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeSRV, dns.TypeRRSIG} + + n.Hdr.Name = dns.HashName(qname, dns.SHA1, 0, "") + buf := packBase32(n.Hdr.Name) + byteArith(buf, true) // one next + n.NextDomain = unpackBase32(buf) + + n.Hdr.Name += appendDomain("", s.config.Domain) + return n +} + +// newNSEC3CEandWildcard returns the NSEC3 for the closest encloser +// and the NSEC3 that denies that wildcard at that level. +func newNSEC3CEandWildcard(apex, ce string, ttl uint32) (*dns.NSEC3, *dns.NSEC3) { + n1 := new(dns.NSEC3) + n1.Hdr.Class = dns.ClassINET + n1.Hdr.Rrtype = dns.TypeNSEC3 + n1.Hdr.Ttl = ttl + n1.Hash = dns.SHA1 + n1.Flags = 0 + n1.Iterations = 0 + n1.Salt = "" + // for the apex we need another bitmap + n1.TypeBitMap = []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeSRV, dns.TypeRRSIG} + prev := dns.HashName(ce, dns.SHA1, n1.Iterations, n1.Salt) + n1.Hdr.Name = strings.ToLower(prev) + "." + apex + buf := packBase32(prev) + byteArith(buf, true) // one next + n1.NextDomain = unpackBase32(buf) + + n2 := new(dns.NSEC3) + n2.Hdr.Class = dns.ClassINET + n2.Hdr.Rrtype = dns.TypeNSEC3 + n2.Hdr.Ttl = ttl + n2.Hash = dns.SHA1 + n2.Flags = 0 + n2.Iterations = 0 + n2.Salt = "" + + prev = dns.HashName("*."+ce, dns.SHA1, n2.Iterations, n2.Salt) + buf = packBase32(prev) + byteArith(buf, false) // one before + n2.Hdr.Name = appendDomain(strings.ToLower(unpackBase32(buf)), apex) + byteArith(buf, true) // one next + byteArith(buf, true) // and another one + n2.NextDomain = unpackBase32(buf) + + return n1, n2 +} + +// byteArith adds either 1 or -1 to b, there is no check for under- or overflow. +func byteArith(b []byte, x bool) { + if x { + for i := len(b) - 1; i >= 0; i-- { + if b[i] == 255 { + b[i] = 0 + continue + } + b[i]++ + return + } + } + for i := len(b) - 1; i >= 0; i-- { + if b[i] == 0 { + b[i] = 255 + continue + } + b[i]-- + return + } +} diff --git a/vendor/github.com/skynetservices/skydns/server/server.go b/vendor/github.com/skynetservices/skydns/server/server.go new file mode 100644 index 00000000000..883afef9972 --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/server.go @@ -0,0 +1,888 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import ( + "fmt" + "math" + "net" + "strconv" + "strings" + "sync" + "time" + + "github.com/skynetservices/skydns/cache" + "github.com/skynetservices/skydns/metrics" + "github.com/skynetservices/skydns/msg" + + etcd "github.com/coreos/etcd/client" + "github.com/coreos/go-systemd/activation" + "github.com/miekg/dns" +) + +const Version = "2.5.3a" + +type server struct { + backend Backend + config *Config + + group *sync.WaitGroup + dnsUDPclient *dns.Client // used for forwarding queries + dnsTCPclient *dns.Client // used for forwarding queries + scache *cache.Cache + rcache *cache.Cache +} + +// New returns a new SkyDNS server. +func New(backend Backend, config *Config) *server { + return &server{ + backend: backend, + config: config, + + group: new(sync.WaitGroup), + scache: cache.New(config.SCache, 0), + rcache: cache.New(config.RCache, config.RCacheTtl), + dnsUDPclient: &dns.Client{Net: "udp", ReadTimeout: config.ReadTimeout, WriteTimeout: config.ReadTimeout, SingleInflight: true}, + dnsTCPclient: &dns.Client{Net: "tcp", ReadTimeout: config.ReadTimeout, WriteTimeout: config.ReadTimeout, SingleInflight: true}, + } +} + +// Run is a blocking operation that starts the server listening on the DNS ports. +func (s *server) Run() error { + mux := dns.NewServeMux() + mux.Handle(".", s) + + dnsReadyMsg := func(addr, net string) { + if s.config.DNSSEC == "" { + logf("ready for queries on %s for %s://%s [rcache %d]", s.config.Domain, net, addr, s.config.RCache) + } else { + logf("ready for queries on %s for %s://%s [rcache %d], signing with %s [scache %d]", s.config.Domain, net, addr, s.config.RCache, s.config.DNSSEC, s.config.SCache) + } + } + + if s.config.Systemd { + packetConns, err := activation.PacketConns(false) + if err != nil { + return err + } + listeners, err := activation.Listeners(true) + if err != nil { + return err + } + if len(packetConns) == 0 && len(listeners) == 0 { + return fmt.Errorf("no UDP or TCP sockets supplied by systemd") + } + for _, p := range packetConns { + if u, ok := p.(*net.UDPConn); ok { + s.group.Add(1) + go func() { + defer s.group.Done() + if err := dns.ActivateAndServe(nil, u, mux); err != nil { + fatalf("%s", err) + } + }() + dnsReadyMsg(u.LocalAddr().String(), "udp") + } + } + for _, l := range listeners { + if t, ok := l.(*net.TCPListener); ok { + s.group.Add(1) + go func() { + defer s.group.Done() + if err := dns.ActivateAndServe(t, nil, mux); err != nil { + fatalf("%s", err) + } + }() + dnsReadyMsg(t.Addr().String(), "tcp") + } + } + } else { + s.group.Add(1) + go func() { + defer s.group.Done() + if err := dns.ListenAndServe(s.config.DnsAddr, "tcp", mux); err != nil { + fatalf("%s", err) + } + }() + dnsReadyMsg(s.config.DnsAddr, "tcp") + s.group.Add(1) + go func() { + defer s.group.Done() + if err := dns.ListenAndServe(s.config.DnsAddr, "udp", mux); err != nil { + fatalf("%s", err) + } + }() + dnsReadyMsg(s.config.DnsAddr, "udp") + } + + s.group.Wait() + return nil +} + +// Stop stops a server. +func (s *server) Stop() { + // TODO(miek) + //s.group.Add(-2) +} + +// ServeDNS is the handler for DNS requests, responsible for parsing DNS request, possibly forwarding +// it to a real dns server and returning a response. +func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { + m := new(dns.Msg) + m.SetReply(req) + m.Authoritative = true + m.RecursionAvailable = true + m.Compress = true + + bufsize := uint16(512) + dnssec := false + tcp := false + start := time.Now() + + q := req.Question[0] + name := strings.ToLower(q.Name) + + if q.Qtype == dns.TypeANY { + m.Authoritative = false + m.Rcode = dns.RcodeRefused + m.RecursionAvailable = false + m.RecursionDesired = false + m.Compress = false + w.WriteMsg(m) + + metrics.ReportRequestCount(m, metrics.Auth) + metrics.ReportDuration(m, start, metrics.Auth) + metrics.ReportErrorCount(m, metrics.Auth) + + return + } + + if o := req.IsEdns0(); o != nil { + bufsize = o.UDPSize() + dnssec = o.Do() + } + if bufsize < 512 { + bufsize = 512 + } + // with TCP we can send 64K + if tcp = isTCP(w); tcp { + bufsize = dns.MaxMsgSize - 1 + } + + if s.config.Verbose { + logf("received DNS Request for %q from %q with type %d", q.Name, w.RemoteAddr(), q.Qtype) + } + + // Check cache first. + m1 := s.rcache.Hit(q, dnssec, tcp, m.Id) + if m1 != nil { + metrics.ReportRequestCount(req, metrics.Cache) + + if send := s.overflowOrTruncated(w, m1, int(bufsize), metrics.Cache); send { + return + } + + // Still round-robin even with hits from the cache. + // Only shuffle A and AAAA records with each other. + if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA { + s.RoundRobin(m1.Answer) + } + + if err := w.WriteMsg(m1); err != nil { + logf("failure to return reply %q", err) + } + + metrics.ReportDuration(m1, start, metrics.Cache) + metrics.ReportErrorCount(m1, metrics.Cache) + return + } + + for zone, ns := range *s.config.stub { + if strings.HasSuffix(name, zone) { + metrics.ReportRequestCount(req, metrics.Stub) + + resp := s.ServeDNSStubForward(w, req, ns) + if resp != nil { + s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) + } + + metrics.ReportDuration(resp, start, metrics.Stub) + metrics.ReportErrorCount(resp, metrics.Stub) + return + } + } + + // If the qname is local.ds.skydns.local. and s.config.Local != "", substitute that name. + if s.config.Local != "" && name == s.config.localDomain { + name = s.config.Local + } + + if q.Qtype == dns.TypePTR && strings.HasSuffix(name, ".in-addr.arpa.") || strings.HasSuffix(name, ".ip6.arpa.") { + metrics.ReportRequestCount(req, metrics.Reverse) + + resp := s.ServeDNSReverse(w, req) + if resp != nil { + s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) + } + + metrics.ReportDuration(resp, start, metrics.Reverse) + metrics.ReportErrorCount(resp, metrics.Reverse) + return + } + + if q.Qclass != dns.ClassCHAOS && !strings.HasSuffix(name, s.config.Domain) { + metrics.ReportRequestCount(req, metrics.Rec) + + resp := s.ServeDNSForward(w, req) + if resp != nil { + s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) + } + + metrics.ReportDuration(resp, start, metrics.Rec) + metrics.ReportErrorCount(resp, metrics.Rec) + return + } + + metrics.ReportCacheMiss(metrics.Response) + + defer func() { + metrics.ReportDuration(m, start, metrics.Auth) + metrics.ReportErrorCount(m, metrics.Auth) + + if m.Rcode == dns.RcodeServerFailure { + if err := w.WriteMsg(m); err != nil { + logf("failure to return reply %q", err) + } + return + } + // Set TTL to the minimum of the RRset and dedup the message, i.e. remove identical RRs. + m = s.dedup(m) + + minttl := s.config.Ttl + if len(m.Answer) > 1 { + for _, r := range m.Answer { + if r.Header().Ttl < minttl { + minttl = r.Header().Ttl + } + } + for _, r := range m.Answer { + r.Header().Ttl = minttl + } + } + + if dnssec { + if s.config.PubKey != nil { + m.AuthenticatedData = true + s.Denial(m) + s.Sign(m, bufsize) + } + } + + if send := s.overflowOrTruncated(w, m, int(bufsize), metrics.Auth); send { + return + } + + s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), m) + + if err := w.WriteMsg(m); err != nil { + logf("failure to return reply %q", err) + } + }() + + if name == s.config.Domain { + if q.Qtype == dns.TypeSOA { + m.Answer = []dns.RR{s.NewSOA()} + return + } + if q.Qtype == dns.TypeDNSKEY { + if s.config.PubKey != nil { + m.Answer = []dns.RR{s.config.PubKey} + return + } + } + } + if q.Qclass == dns.ClassCHAOS { + if q.Qtype == dns.TypeTXT { + switch name { + case "authors.bind.": + fallthrough + case s.config.Domain: + hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} + authors := []string{"Erik St. Martin", "Brian Ketelsen", "Miek Gieben", "Michael Crosby"} + for _, a := range authors { + m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{a}}) + } + for j := 0; j < len(authors)*(int(dns.Id())%4+1); j++ { + q := int(dns.Id()) % len(authors) + p := int(dns.Id()) % len(authors) + if q == p { + p = (p + 1) % len(authors) + } + m.Answer[q], m.Answer[p] = m.Answer[p], m.Answer[q] + } + return + case "version.bind.": + fallthrough + case "version.server.": + hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} + m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{Version}}} + return + case "hostname.bind.": + fallthrough + case "id.server.": + // TODO(miek): machine name to return + hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0} + m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}} + return + } + } + // still here, fail + m.SetReply(req) + m.SetRcode(req, dns.RcodeServerFailure) + return + } + + switch q.Qtype { + case dns.TypeNS: + if name != s.config.Domain { + break + } + // Lookup s.config.DnsDomain + records, extra, err := s.NSRecords(q, s.config.dnsDomain) + if isEtcdNameError(err, s) { + m = s.NameError(req) + return + } + m.Answer = append(m.Answer, records...) + m.Extra = append(m.Extra, extra...) + case dns.TypeA, dns.TypeAAAA: + records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false) + if isEtcdNameError(err, s) { + m = s.NameError(req) + return + } + m.Answer = append(m.Answer, records...) + case dns.TypeTXT: + records, err := s.TXTRecords(q, name) + if isEtcdNameError(err, s) { + m = s.NameError(req) + return + } + m.Answer = append(m.Answer, records...) + case dns.TypeCNAME: + records, err := s.CNAMERecords(q, name) + if isEtcdNameError(err, s) { + m = s.NameError(req) + return + } + m.Answer = append(m.Answer, records...) + case dns.TypeMX: + records, extra, err := s.MXRecords(q, name, bufsize, dnssec) + if isEtcdNameError(err, s) { + m = s.NameError(req) + return + } + m.Answer = append(m.Answer, records...) + m.Extra = append(m.Extra, extra...) + default: + fallthrough // also catch other types, so that they return NODATA + case dns.TypeSRV: + records, extra, err := s.SRVRecords(q, name, bufsize, dnssec) + if err != nil { + if isEtcdNameError(err, s) { + m = s.NameError(req) + return + } + logf("got error from backend: %s", err) + if q.Qtype == dns.TypeSRV { // Otherwise NODATA + m = s.ServerFailure(req) + return + } + } + // if we are here again, check the types, because an answer may only + // be given for SRV. All other types should return NODATA, the + // NXDOMAIN part is handled in the above code. TODO(miek): yes this + // can be done in a more elegant manor. + if q.Qtype == dns.TypeSRV { + m.Answer = append(m.Answer, records...) + m.Extra = append(m.Extra, extra...) + } + } + + if len(m.Answer) == 0 { // NODATA response + m.Ns = []dns.RR{s.NewSOA()} + m.Ns[0].Header().Ttl = s.config.MinTtl + } +} + +func (s *server) AddressRecords(q dns.Question, name string, previousRecords []dns.RR, bufsize uint16, dnssec, both bool) (records []dns.RR, err error) { + services, err := s.backend.Records(name, false) + if err != nil { + return nil, err + } + + services = msg.Group(services) + + for _, serv := range services { + ip := net.ParseIP(serv.Host) + switch { + case ip == nil: + // Try to resolve as CNAME if it's not an IP, but only if we don't create loops. + if q.Name == dns.Fqdn(serv.Host) { + // x CNAME x is a direct loop, don't add those + continue + } + + newRecord := serv.NewCNAME(q.Name, dns.Fqdn(serv.Host)) + if len(previousRecords) > 7 { + logf("CNAME lookup limit of 8 exceeded for %s", newRecord) + // don't add it, and just continue + continue + } + if s.isDuplicateCNAME(newRecord, previousRecords) { + logf("CNAME loop detected for record %s", newRecord) + continue + } + + nextRecords, err := s.AddressRecords(dns.Question{Name: dns.Fqdn(serv.Host), Qtype: q.Qtype, Qclass: q.Qclass}, + strings.ToLower(dns.Fqdn(serv.Host)), append(previousRecords, newRecord), bufsize, dnssec, both) + if err == nil { + // Only have we found something we should add the CNAME and the IP addresses. + if len(nextRecords) > 0 { + records = append(records, newRecord) + records = append(records, nextRecords...) + } + continue + } + // This means we can not complete the CNAME, try to look else where. + target := newRecord.Target + if dns.IsSubDomain(s.config.Domain, target) { + // We should already have found it + continue + } + m1, e1 := s.Lookup(target, q.Qtype, bufsize, dnssec) + if e1 != nil { + logf("incomplete CNAME chain: %s", e1) + continue + } + // Len(m1.Answer) > 0 here is well? + records = append(records, newRecord) + records = append(records, m1.Answer...) + continue + case ip.To4() != nil && (q.Qtype == dns.TypeA || both): + records = append(records, serv.NewA(q.Name, ip.To4())) + case ip.To4() == nil && (q.Qtype == dns.TypeAAAA || both): + records = append(records, serv.NewAAAA(q.Name, ip.To16())) + } + } + s.RoundRobin(records) + return records, nil +} + +// NSRecords returns NS records from etcd. +func (s *server) NSRecords(q dns.Question, name string) (records []dns.RR, extra []dns.RR, err error) { + services, err := s.backend.Records(name, false) + if err != nil { + return nil, nil, err + } + + services = msg.Group(services) + + for _, serv := range services { + ip := net.ParseIP(serv.Host) + switch { + case ip == nil: + return nil, nil, fmt.Errorf("NS record must be an IP address") + case ip.To4() != nil: + serv.Host = msg.Domain(serv.Key) + records = append(records, serv.NewNS(q.Name, serv.Host)) + extra = append(extra, serv.NewA(serv.Host, ip.To4())) + case ip.To4() == nil: + serv.Host = msg.Domain(serv.Key) + records = append(records, serv.NewNS(q.Name, serv.Host)) + extra = append(extra, serv.NewAAAA(serv.Host, ip.To16())) + } + } + return records, extra, nil +} + +// SRVRecords returns SRV records from etcd. +// If the Target is not a name but an IP address, a name is created. +func (s *server) SRVRecords(q dns.Question, name string, bufsize uint16, dnssec bool) (records []dns.RR, extra []dns.RR, err error) { + services, err := s.backend.Records(name, false) + if err != nil { + return nil, nil, err + } + + services = msg.Group(services) + + // Looping twice to get the right weight vs priority + w := make(map[int]int) + for _, serv := range services { + weight := 100 + if serv.Weight != 0 { + weight = serv.Weight + } + if _, ok := w[serv.Priority]; !ok { + w[serv.Priority] = weight + continue + } + w[serv.Priority] += weight + } + lookup := make(map[string]bool) + for _, serv := range services { + w1 := 100.0 / float64(w[serv.Priority]) + if serv.Weight == 0 { + w1 *= 100 + } else { + w1 *= float64(serv.Weight) + } + weight := uint16(math.Floor(w1)) + ip := net.ParseIP(serv.Host) + switch { + case ip == nil: + srv := serv.NewSRV(q.Name, weight) + records = append(records, srv) + + if _, ok := lookup[srv.Target]; ok { + break + } + + lookup[srv.Target] = true + + if !dns.IsSubDomain(s.config.Domain, srv.Target) { + m1, e1 := s.Lookup(srv.Target, dns.TypeA, bufsize, dnssec) + if e1 == nil { + extra = append(extra, m1.Answer...) + } + m1, e1 = s.Lookup(srv.Target, dns.TypeAAAA, bufsize, dnssec) + if e1 == nil { + // If we have seen CNAME's we *assume* that they are already added. + for _, a := range m1.Answer { + if _, ok := a.(*dns.CNAME); !ok { + extra = append(extra, a) + } + } + } + break + } + // Internal name, we should have some info on them, either v4 or v6 + // Clients expect a complete answer, because we are a recursor in their + // view. + addr, e1 := s.AddressRecords(dns.Question{srv.Target, dns.ClassINET, dns.TypeA}, + srv.Target, nil, bufsize, dnssec, true) + if e1 == nil { + extra = append(extra, addr...) + } + case ip.To4() != nil: + serv.Host = msg.Domain(serv.Key) + srv := serv.NewSRV(q.Name, weight) + + records = append(records, srv) + extra = append(extra, serv.NewA(srv.Target, ip.To4())) + case ip.To4() == nil: + serv.Host = msg.Domain(serv.Key) + srv := serv.NewSRV(q.Name, weight) + + records = append(records, srv) + extra = append(extra, serv.NewAAAA(srv.Target, ip.To16())) + } + } + return records, extra, nil +} + +// MXRecords returns MX records from etcd. +// If the Target is not a name but an IP address, a name is created. +func (s *server) MXRecords(q dns.Question, name string, bufsize uint16, dnssec bool) (records []dns.RR, extra []dns.RR, err error) { + services, err := s.backend.Records(name, false) + if err != nil { + return nil, nil, err + } + + lookup := make(map[string]bool) + for _, serv := range services { + if !serv.Mail { + continue + } + ip := net.ParseIP(serv.Host) + switch { + case ip == nil: + mx := serv.NewMX(q.Name) + records = append(records, mx) + if _, ok := lookup[mx.Mx]; ok { + break + } + + lookup[mx.Mx] = true + + if !dns.IsSubDomain(s.config.Domain, mx.Mx) { + m1, e1 := s.Lookup(mx.Mx, dns.TypeA, bufsize, dnssec) + if e1 == nil { + extra = append(extra, m1.Answer...) + } + m1, e1 = s.Lookup(mx.Mx, dns.TypeAAAA, bufsize, dnssec) + if e1 == nil { + // If we have seen CNAME's we *assume* that they are already added. + for _, a := range m1.Answer { + if _, ok := a.(*dns.CNAME); !ok { + extra = append(extra, a) + } + } + } + break + } + // Internal name + addr, e1 := s.AddressRecords(dns.Question{mx.Mx, dns.ClassINET, dns.TypeA}, + mx.Mx, nil, bufsize, dnssec, true) + if e1 == nil { + extra = append(extra, addr...) + } + case ip.To4() != nil: + serv.Host = msg.Domain(serv.Key) + records = append(records, serv.NewMX(q.Name)) + extra = append(extra, serv.NewA(serv.Host, ip.To4())) + case ip.To4() == nil: + serv.Host = msg.Domain(serv.Key) + records = append(records, serv.NewMX(q.Name)) + extra = append(extra, serv.NewAAAA(serv.Host, ip.To16())) + } + } + return records, extra, nil +} + +func (s *server) CNAMERecords(q dns.Question, name string) (records []dns.RR, err error) { + services, err := s.backend.Records(name, true) + if err != nil { + return nil, err + } + + services = msg.Group(services) + + if len(services) > 0 { + serv := services[0] + if ip := net.ParseIP(serv.Host); ip == nil { + records = append(records, serv.NewCNAME(q.Name, dns.Fqdn(serv.Host))) + } + } + return records, nil +} + +func (s *server) TXTRecords(q dns.Question, name string) (records []dns.RR, err error) { + services, err := s.backend.Records(name, false) + if err != nil { + return nil, err + } + + services = msg.Group(services) + + for _, serv := range services { + if serv.Text == "" { + continue + } + records = append(records, serv.NewTXT(q.Name)) + } + return records, nil +} + +func (s *server) PTRRecords(q dns.Question) (records []dns.RR, err error) { + name := strings.ToLower(q.Name) + serv, err := s.backend.ReverseRecord(name) + if err != nil { + return nil, err + } + + records = append(records, serv.NewPTR(q.Name, serv.Ttl)) + return records, nil +} + +// SOA returns a SOA record for this SkyDNS instance. +func (s *server) NewSOA() dns.RR { + return &dns.SOA{Hdr: dns.RR_Header{Name: s.config.Domain, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: s.config.Ttl}, + Ns: appendDomain("ns.dns", s.config.Domain), + Mbox: s.config.Hostmaster, + Serial: uint32(time.Now().Truncate(time.Hour).Unix()), + Refresh: 28800, + Retry: 7200, + Expire: 604800, + Minttl: s.config.MinTtl, + } +} + +func (s *server) isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool { + for _, rec := range records { + if v, ok := rec.(*dns.CNAME); ok { + if v.Target == r.Target { + return true + } + } + } + return false +} + +func (s *server) NameError(req *dns.Msg) *dns.Msg { + m := new(dns.Msg) + m.SetRcode(req, dns.RcodeNameError) + m.Ns = []dns.RR{s.NewSOA()} + m.Ns[0].Header().Ttl = s.config.MinTtl + return m +} + +func (s *server) ServerFailure(req *dns.Msg) *dns.Msg { + m := new(dns.Msg) + m.SetRcode(req, dns.RcodeServerFailure) + return m +} + +func (s *server) RoundRobin(rrs []dns.RR) { + if !s.config.RoundRobin { + return + } + // If we have more than 1 CNAME don't touch the packet, because some stub resolver (=glibc) + // can't deal with the returned packet if the CNAMEs need to be accesses in the reverse order. + cname := 0 + for _, r := range rrs { + if r.Header().Rrtype == dns.TypeCNAME { + cname++ + if cname > 1 { + return + } + } + } + + switch l := len(rrs); l { + case 2: + if dns.Id()%2 == 0 { + rrs[0], rrs[1] = rrs[1], rrs[0] + } + default: + for j := 0; j < l*(int(dns.Id())%4+1); j++ { + q := int(dns.Id()) % l + p := int(dns.Id()) % l + if q == p { + p = (p + 1) % l + } + rrs[q], rrs[p] = rrs[p], rrs[q] + } + } + +} + +// dedup will de-duplicate a message on a per section basis. +// Multiple identical (same name, class, type and rdata) RRs will be coalesced into one. +func (s *server) dedup(m *dns.Msg) *dns.Msg { + // Answer section + ma := make(map[string]dns.RR) + for _, a := range m.Answer { + // Or use Pack()... Think this function also could be placed in go dns. + s1 := a.Header().Name + s1 += strconv.Itoa(int(a.Header().Class)) + s1 += strconv.Itoa(int(a.Header().Rrtype)) + // there can only be one CNAME for an ownername + if a.Header().Rrtype == dns.TypeCNAME { + if _, ok := ma[s1]; ok { + // already exist, randomly overwrite if roundrobin is true + // Note: even with roundrobin *off* this depends on the + // order we get the names. + if s.config.RoundRobin && dns.Id()%2 == 0 { + ma[s1] = a + continue + } + } + ma[s1] = a + continue + } + for i := 1; i <= dns.NumField(a); i++ { + s1 += dns.Field(a, i) + } + ma[s1] = a + } + // Only is our map is smaller than the #RR in the answer section we should reset the RRs + // in the section it self + if len(ma) < len(m.Answer) { + i := 0 + for _, v := range ma { + m.Answer[i] = v + i++ + } + m.Answer = m.Answer[:len(ma)] + } + + // Additional section + me := make(map[string]dns.RR) + for _, e := range m.Extra { + s1 := e.Header().Name + s1 += strconv.Itoa(int(e.Header().Class)) + s1 += strconv.Itoa(int(e.Header().Rrtype)) + // there can only be one CNAME for an ownername + if e.Header().Rrtype == dns.TypeCNAME { + if _, ok := me[s1]; ok { + // already exist, randomly overwrite if roundrobin is true + if s.config.RoundRobin && dns.Id()%2 == 0 { + me[s1] = e + continue + } + } + me[s1] = e + continue + } + for i := 1; i <= dns.NumField(e); i++ { + s1 += dns.Field(e, i) + } + me[s1] = e + } + + if len(me) < len(m.Extra) { + i := 0 + for _, v := range me { + m.Extra[i] = v + i++ + } + m.Extra = m.Extra[:len(me)] + } + + return m +} + +// overflowOrTruncated writes back an error to the client if the message does not fit. +// It updates prometheus metrics. If something has been written to the client, true +// will be returned. +func (s *server) overflowOrTruncated(w dns.ResponseWriter, m *dns.Msg, bufsize int, sy metrics.System) bool { + switch isTCP(w) { + case true: + if _, overflow := Fit(m, dns.MaxMsgSize, true); overflow { + metrics.ReportErrorCount(m, sy) + msgFail := s.ServerFailure(m) + w.WriteMsg(msgFail) + return true + } + case false: + // Overflow with udp always results in TC. + Fit(m, bufsize, false) + metrics.ReportErrorCount(m, sy) + if m.Truncated { + w.WriteMsg(m) + return true + } + } + return false +} + +// isTCP returns true if the client is connecting over TCP. +func isTCP(w dns.ResponseWriter) bool { + _, ok := w.RemoteAddr().(*net.TCPAddr) + return ok +} + +// etcNameError return a NameError to the client if the error +// returned from etcd has ErrorCode == 100. +func isEtcdNameError(err error, s *server) bool { + if e, ok := err.(etcd.Error); ok && e.Code == etcd.ErrorCodeKeyNotFound { + return true + } + if err != nil { + logf("error from backend: %s", err) + } + return false +} diff --git a/vendor/github.com/skynetservices/skydns/server/stub.go b/vendor/github.com/skynetservices/skydns/server/stub.go new file mode 100644 index 00000000000..6e8b38c0cac --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/server/stub.go @@ -0,0 +1,124 @@ +// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. +// Use of this source code is governed by The MIT License (MIT) that can be +// found in the LICENSE file. + +package server + +import ( + "net" + "strconv" + "strings" + + "github.com/miekg/dns" + "github.com/skynetservices/skydns/msg" +) + +const ednsStubCode = dns.EDNS0LOCALSTART + 10 + +// ednsStub is the EDNS0 record we add to stub queries. Queries which have this record are +// not forwarded again. +var ednsStub = func() *dns.OPT { + o := new(dns.OPT) + o.Hdr.Name = "." + o.Hdr.Rrtype = dns.TypeOPT + e := new(dns.EDNS0_LOCAL) + e.Code = ednsStubCode + e.Data = []byte{1} + o.Option = append(o.Option, e) + return o +}() + +// Look in .../dns/stub//xx for msg.Services. Loop through them +// extract and add them as forwarders (ip:port-combos) for +// the stub zones. Only numeric (i.e. IP address) hosts are used. +func (s *server) UpdateStubZones() { + stubmap := make(map[string][]string) + + services, err := s.backend.Records("stub.dns."+s.config.Domain, false) + if err != nil { + logf("stub zone update failed: %s", err) + return + } + for _, serv := range services { + if serv.Port == 0 { + serv.Port = 53 + } + ip := net.ParseIP(serv.Host) + if ip == nil { + logf("stub zone non-address %s seen for: %s", serv.Key, serv.Host) + continue + } + + domain := msg.Domain(serv.Key) + // Chop of left most label, because that is used as the nameserver place holder + // and drop the right most labels that belong to localDomain. + labels := dns.SplitDomainName(domain) + domain = dns.Fqdn(strings.Join(labels[1:len(labels)-dns.CountLabel(s.config.localDomain)], ".")) + + // If the remaining name equals s.config.LocalDomain we ignore it. + if domain == s.config.localDomain { + logf("not adding stub zone for my own domain") + continue + } + stubmap[domain] = append(stubmap[domain], net.JoinHostPort(serv.Host, strconv.Itoa(serv.Port))) + } + + s.config.stub = &stubmap +} + +// ServeDNSStubForward forwards a request to a nameservers and returns the response. +func (s *server) ServeDNSStubForward(w dns.ResponseWriter, req *dns.Msg, ns []string) *dns.Msg { + // Check EDNS0 Stub option, if set drop the packet. + option := req.IsEdns0() + if option != nil { + for _, o := range option.Option { + if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 && + o.(*dns.EDNS0_LOCAL).Data[0] == 1 { + // Maybe log source IP here? + logf("not fowarding stub request to another stub") + return nil + } + } + } + + // Add a custom EDNS0 option to the packet, so we can detect loops + // when 2 stubs are forwarding to each other. + if option != nil { + option.Option = append(option.Option, &dns.EDNS0_LOCAL{ednsStubCode, []byte{1}}) + } else { + req.Extra = append(req.Extra, ednsStub) + } + + var ( + r *dns.Msg + err error + ) + + // Use request Id for "random" nameserver selection. + nsid := int(req.Id) % len(ns) + try := 0 +Redo: + if isTCP(w) { + r, err = exchangeWithRetry(s.dnsTCPclient, req, ns[nsid]) + } else { + r, err = exchangeWithRetry(s.dnsUDPclient, req, ns[nsid]) + } + if err == nil { + r.Compress = true + r.Id = req.Id + w.WriteMsg(r) + return r + } + // Seen an error, this can only mean, "server not reached", try again + // but only if we have not exausted our nameservers. + if try < len(ns) { + try++ + nsid = (nsid + 1) % len(ns) + goto Redo + } + + logf("failure to forward stub request %q", err) + m := s.ServerFailure(req) + w.WriteMsg(m) + return m +} diff --git a/vendor/github.com/skynetservices/skydns/singleflight/singleflight.go b/vendor/github.com/skynetservices/skydns/singleflight/singleflight.go new file mode 100644 index 00000000000..ff2c2ee4f3d --- /dev/null +++ b/vendor/github.com/skynetservices/skydns/singleflight/singleflight.go @@ -0,0 +1,64 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package singleflight provides a duplicate function call suppression +// mechanism. +package singleflight + +import "sync" + +// call is an in-flight or completed Do call +type call struct { + wg sync.WaitGroup + val interface{} + err error +} + +// Group represents a class of work and forms a namespace in which +// units of work can be executed with duplicate suppression. +type Group struct { + mu 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. +func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if c, ok := g.m[key]; ok { + g.mu.Unlock() + c.wg.Wait() + return c.val, c.err + } + c := new(call) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + c.val, c.err = fn() + c.wg.Done() + + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() + + return c.val, c.err +} From 3ada2170a320b4bee15d6b1d372d0f9e0cadb838 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Wed, 18 May 2016 10:33:17 -0700 Subject: [PATCH 6/6] pr feedback --- .../saltbase/salt/kube-dns/kubedns-rc.yaml.in | 5 +- cmd/kube-dns/app/options/options.go | 6 +- cmd/kube-dns/app/server.go | 11 +- cmd/kube-dns/dns.go | 4 +- pkg/dns/dns.go | 136 ++++++---- pkg/dns/dns_test.go | 52 ++-- pkg/dns/treecache.go | 245 +++++++----------- 7 files changed, 218 insertions(+), 241 deletions(-) diff --git a/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in b/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in index 631939edc6f..985d1066e4e 100644 --- a/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in +++ b/cluster/saltbase/salt/kube-dns/kubedns-rc.yaml.in @@ -19,12 +19,9 @@ spec: version: v12 kubernetes.io/cluster-service: "true" spec: -{% if grains['cloud'] is defined and grains['cloud'] in [ 'vsphere', 'photon-controller' ] %} - hostNetwork: true -{% endif %} containers: - name: kubedns - image: artfulcoder/kubedns-amd64:1.0 + image: gcr.io/google_containers/kubedns-amd64:1.0 resources: # TODO: Set memory limits when we've profiled the container for large # clusters, then set request = limit to keep this container in diff --git a/cmd/kube-dns/app/options/options.go b/cmd/kube-dns/app/options/options.go index 973a24901a9..8991ae2e6f0 100644 --- a/cmd/kube-dns/app/options/options.go +++ b/cmd/kube-dns/app/options/options.go @@ -1,5 +1,5 @@ /* -Copyright 2014 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -52,8 +52,8 @@ func (m clusterDomainVar) Set(v string) error { v = strings.TrimSuffix(v, ".") segments := strings.Split(v, ".") for _, segment := range segments { - if !validation.IsDNS1123Label(segment) { - return fmt.Errorf("Not a valid DNS label") + if errs := validation.IsDNS1123Label(segment); len(errs) > 0 { + return fmt.Errorf("Not a valid DNS label. %v", errs) } } if !strings.HasSuffix(v, ".") { diff --git a/cmd/kube-dns/app/server.go b/cmd/kube-dns/app/server.go index d7d3d20f1d8..07637469d2f 100644 --- a/cmd/kube-dns/app/server.go +++ b/cmd/kube-dns/app/server.go @@ -1,5 +1,5 @@ /* -Copyright 2014 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import ( "syscall" "github.com/golang/glog" - "github.com/skynetservices/skydns/metrics" "github.com/skynetservices/skydns/server" "k8s.io/kubernetes/cmd/kube-dns/app/options" @@ -104,7 +103,13 @@ func (server *KubeDNSServer) setupHealthzHandlers() { fmt.Fprintf(w, "ok\n") }) http.HandleFunc("/cache", func(w http.ResponseWriter, req *http.Request) { - fmt.Fprint(w, server.kd.GetCacheAsJSON()) + serializedJSON, err := server.kd.GetCacheAsJSON() + if err == nil { + fmt.Fprint(w, serializedJSON) + } else { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, err) + } }) } diff --git a/cmd/kube-dns/dns.go b/cmd/kube-dns/dns.go index 07d4661d855..2e08a002939 100644 --- a/cmd/kube-dns/dns.go +++ b/cmd/kube-dns/dns.go @@ -1,5 +1,5 @@ /* -Copyright 2014 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,11 +23,9 @@ import ( "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/flag" "k8s.io/kubernetes/pkg/version/verflag" - "runtime" ) func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) config := options.NewKubeDNSConfig() config.AddFlags(pflag.CommandLine) diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go index 6b184ce26c8..d991c510f55 100644 --- a/pkg/dns/dns.go +++ b/pkg/dns/dns.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,13 +19,14 @@ package dns import ( "encoding/json" "fmt" - "github.com/golang/glog" "hash/fnv" "net" "strings" + "sync" "time" etcd "github.com/coreos/etcd/client" + "github.com/golang/glog" skymsg "github.com/skynetservices/skydns/msg" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/endpoints" @@ -47,20 +48,40 @@ const ( podSubdomain = "pod" // Resync period for the kube controller loop. - resyncPeriod = 30 * time.Minute + resyncPeriod = 5 * time.Minute ) type KubeDNS struct { + // kubeClient makes calls to API Server and registers calls with API Server + // to get Endpoints and Service objects. kubeClient *kclient.Client - // DNS domain name. + + // The domain for which this DNS Server is authoritative. domain string + // A cache that contains all the endpoints in the system. endpointsStore kcache.Store + // A cache that contains all the services in the system. - servicesStore kcache.Store - cache *TreeCache - domainPath []string - eController *kframework.Controller + servicesStore kcache.Store + + // stores DNS records for the domain. + // A Records and SRV Records for (regular) services and headless Services. + cache *TreeCache + + // caller is responsible for using the cacheLock before invoking methods on cache + // the cache is not thread-safe, and the caller can guarantee thread safety by using + // the cacheLock + cacheLock sync.RWMutex + + // The domain for which this DNS Server is authoritative, in array format and reversed. + // e.g. if domain is "cluster.local", domainPath is []string{"local", "cluster"} + domainPath []string + + // endpointsController invokes registered callbacks when endpoints change. + endpointsController *kframework.Controller + + // serviceController invokes registered callbacks when services change. serviceController *kframework.Controller } @@ -69,6 +90,7 @@ func NewKubeDNS(client *kclient.Client, domain string) *KubeDNS { kubeClient: client, domain: domain, cache: NewTreeCache(), + cacheLock: sync.RWMutex{}, domainPath: reverseArray(strings.Split(strings.TrimRight(domain, "."), ".")), } kd.setEndpointsStore() @@ -77,11 +99,14 @@ func NewKubeDNS(client *kclient.Client, domain string) *KubeDNS { } func (kd *KubeDNS) Start() { - go kd.eController.Run(wait.NeverStop) + go kd.endpointsController.Run(wait.NeverStop) go kd.serviceController.Run(wait.NeverStop) // Wait synchronously for the Kubernetes service and add a DNS record for it. - // TODO (abshah) UNCOMMENT AFTER TEST COMPLETE - //kd.waitForKubernetesService() + // This ensures that the Start function returns only after having received Service objects + // from APIServer. + // TODO: we might not have to wait for kubernetes service specifically. We should just wait + // for a list operation to be complete from APIServer. + kd.waitForKubernetesService() } func (kd *KubeDNS) waitForKubernetesService() (svc *kapi.Service) { @@ -101,9 +126,11 @@ func (kd *KubeDNS) waitForKubernetesService() (svc *kapi.Service) { return } -func (kd *KubeDNS) GetCacheAsJSON() string { - json, _ := kd.cache.Serialize("") - return json +func (kd *KubeDNS) GetCacheAsJSON() (string, error) { + kd.cacheLock.RLock() + defer kd.cacheLock.RUnlock() + json, err := kd.cache.Serialize() + return json, err } func (kd *KubeDNS) setServicesStore() { @@ -124,7 +151,7 @@ func (kd *KubeDNS) setServicesStore() { func (kd *KubeDNS) setEndpointsStore() { // Returns a cache.ListWatch that gets all changes to endpoints. endpointsWatch := kcache.NewListWatchFromClient(kd.kubeClient, "endpoints", kapi.NamespaceAll, kselector.Everything()) - kd.endpointsStore, kd.eController = kframework.NewInformer( + kd.endpointsStore, kd.endpointsController = kframework.NewInformer( endpointsWatch, &kapi.Endpoints{}, resyncPeriod, @@ -138,24 +165,35 @@ func (kd *KubeDNS) setEndpointsStore() { ) } -func (kd *KubeDNS) newService(obj interface{}) { +func assertIsService(obj interface{}) (*kapi.Service, bool) { if service, ok := obj.(*kapi.Service); ok { + return service, ok + } else { + glog.Errorf("Type assertion failed! Expected 'Service', got %T", service) + return nil, ok + } +} + +func (kd *KubeDNS) newService(obj interface{}) { + if service, ok := assertIsService(obj); ok { // if ClusterIP is not set, a DNS entry should not be created if !kapi.IsServiceIPSet(service) { kd.newHeadlessService(service) return } if len(service.Spec.Ports) == 0 { - glog.Info("Unexpected service with no ports, this should not have happend: %v", service) + glog.Warning("Unexpected service with no ports, this should not have happend: %v", service) } kd.newPortalService(service) } } func (kd *KubeDNS) removeService(obj interface{}) { - if s, ok := obj.(*kapi.Service); ok { + if s, ok := assertIsService(obj); ok { subCachePath := append(kd.domainPath, serviceSubdomain, s.Namespace, s.Name) - kd.cache.DeletePath(subCachePath...) + kd.cacheLock.Lock() + defer kd.cacheLock.Unlock() + kd.cache.deletePath(subCachePath...) } } @@ -194,7 +232,7 @@ func (kd *KubeDNS) getServiceFromEndpoints(e *kapi.Endpoints) (*kapi.Service, er glog.V(1).Infof("could not find service for endpoint %q in namespace %q", e.Name, e.Namespace) return nil, nil } - if svc, ok := obj.(*kapi.Service); ok { + if svc, ok := assertIsService(obj); ok { return svc, nil } return nil, fmt.Errorf("got a non service object in services store %v", obj) @@ -203,18 +241,20 @@ func (kd *KubeDNS) getServiceFromEndpoints(e *kapi.Endpoints) (*kapi.Service, er func (kd *KubeDNS) newPortalService(service *kapi.Service) { subCache := NewTreeCache() recordValue, recordLabel := getSkyMsg(service.Spec.ClusterIP, 0) - subCache.SetEntry(recordLabel, recordValue) + subCache.setEntry(recordLabel, recordValue) // Generate SRV Records for i := range service.Spec.Ports { port := &service.Spec.Ports[i] if port.Name != "" && port.Protocol != "" { srvValue := kd.generateSRVRecordValue(service, int(port.Port)) - subCache.SetEntry(recordLabel, srvValue, "_"+strings.ToLower(string(port.Protocol)), "_"+port.Name) + subCache.setEntry(recordLabel, srvValue, "_"+strings.ToLower(string(port.Protocol)), "_"+port.Name) } } subCachePath := append(kd.domainPath, serviceSubdomain, service.Namespace) - kd.cache.SetSubCache(service.Name, subCache, subCachePath...) + kd.cacheLock.Lock() + defer kd.cacheLock.Unlock() + kd.cache.setSubCache(service.Name, subCache, subCachePath...) } func (kd *KubeDNS) generateRecordsForHeadlessService(e *kapi.Endpoints, svc *kapi.Service) error { @@ -233,18 +273,20 @@ func (kd *KubeDNS) generateRecordsForHeadlessService(e *kapi.Endpoints, svc *kap if hostLabel, exists := getHostname(address, podHostnames); exists { endpointName = hostLabel } - subCache.SetEntry(endpointName, recordValue) + subCache.setEntry(endpointName, recordValue) for portIdx := range e.Subsets[idx].Ports { endpointPort := &e.Subsets[idx].Ports[portIdx] if endpointPort.Name != "" && endpointPort.Protocol != "" { srvValue := kd.generateSRVRecordValue(svc, int(endpointPort.Port), endpointName) - subCache.SetEntry(endpointName, srvValue, "_"+strings.ToLower(string(endpointPort.Protocol)), "_"+endpointPort.Name) + subCache.setEntry(endpointName, srvValue, "_"+strings.ToLower(string(endpointPort.Protocol)), "_"+endpointPort.Name) } } } } subCachePath := append(kd.domainPath, serviceSubdomain, svc.Namespace) - kd.cache.SetSubCache(svc.Name, subCache, subCachePath...) + kd.cacheLock.Lock() + defer kd.cacheLock.Unlock() + kd.cache.setSubCache(svc.Name, subCache, subCachePath...) return nil } @@ -252,7 +294,7 @@ func getHostname(address *kapi.EndpointAddress, podHostnames map[string]endpoint if len(address.Hostname) > 0 { return address.Hostname, true } - if hostRecord, exists := podHostnames[address.IP]; exists && validation.IsDNS1123Label(hostRecord.HostName) { + if hostRecord, exists := podHostnames[address.IP]; exists && len(validation.IsDNS1123Label(hostRecord.HostName)) == 0 { return hostRecord.HostName, true } return "", false @@ -272,12 +314,12 @@ func getPodHostnamesFromAnnotation(annotations map[string]string) (map[string]en return hostnames, nil } -func (kd *KubeDNS) generateSRVRecordValue(svc *kapi.Service, portNumber int, cNameLabels ...string) *skymsg.Service { - cName := strings.Join([]string{svc.Name, svc.Namespace, serviceSubdomain, kd.domain}, ".") - for _, cNameLabel := range cNameLabels { - cName = cNameLabel + "." + cName +func (kd *KubeDNS) generateSRVRecordValue(svc *kapi.Service, portNumber int, labels ...string) *skymsg.Service { + host := strings.Join([]string{svc.Name, svc.Namespace, serviceSubdomain, kd.domain}, ".") + for _, cNameLabel := range labels { + host = cNameLabel + "." + host } - recordValue, _ := getSkyMsg(cName, portNumber) + recordValue, _ := getSkyMsg(host, portNumber) return recordValue } @@ -312,9 +354,10 @@ func (kd *KubeDNS) Records(name string, exact bool) ([]skymsg.Service, error) { segments := strings.Split(trimmed, ".") path := reverseArray(segments) if kd.isPodRecord(path) { - response, err := kd.getPodRecord(path) + ip, err := kd.getPodIP(path) if err == nil { - return []skymsg.Service{*response}, nil + skyMsg, _ := getSkyMsg(ip, 0) + return []skymsg.Service{*skyMsg}, nil } return nil, err } @@ -324,15 +367,17 @@ func (kd *KubeDNS) Records(name string, exact bool) ([]skymsg.Service, error) { if key == "" { return []skymsg.Service{}, nil } - if record, ok := kd.cache.GetEntry(key, path[:len(path)-1]...); ok { + kd.cacheLock.RLock() + defer kd.cacheLock.RUnlock() + if record, ok := kd.cache.getEntry(key, path[:len(path)-1]...); ok { return []skymsg.Service{*(record.(*skymsg.Service))}, nil } return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound} } - // tmp, _ := kd.cache.Serialize("") - // glog.Infof("Searching path:%q, %v", path, tmp) - records := kd.cache.GetValuesForPathWithRegex(path...) + kd.cacheLock.RLock() + defer kd.cacheLock.RUnlock() + records := kd.cache.getValuesForPathWithWildcards(path...) retval := []skymsg.Service{} for _, val := range records { retval = append(retval, *(val.(*skymsg.Service))) @@ -350,7 +395,7 @@ func (kd *KubeDNS) ReverseRecord(name string) (*skymsg.Service, error) { segments := strings.Split(strings.TrimRight(name, "."), ".") for _, k := range segments { - if k == "*" || k == "any" { + if k == "*" { return nil, fmt.Errorf("reverse can not contain wildcards") } } @@ -374,20 +419,13 @@ func (kd *KubeDNS) isPodRecord(path []string) bool { return true } -func (kd *KubeDNS) getPodRecord(path []string) (*skymsg.Service, error) { +func (kd *KubeDNS) getPodIP(path []string) (string, error) { ipStr := path[len(path)-1] ip := strings.Replace(ipStr, "-", ".", -1) if parsed := net.ParseIP(ip); parsed != nil { - msg := &skymsg.Service{ - Host: ip, - Port: 0, - Priority: 10, - Weight: 10, - Ttl: 30, - } - return msg, nil + return ip, nil } - return nil, fmt.Errorf("Invalid IP Address %v", ip) + return "", fmt.Errorf("Invalid IP Address %v", ip) } // Returns record in a format that SkyDNS understands. diff --git a/pkg/dns/dns_test.go b/pkg/dns/dns_test.go index 9a7a6e61e90..7aa4316804e 100644 --- a/pkg/dns/dns_test.go +++ b/pkg/dns/dns_test.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ package dns import ( "fmt" + "net" "strings" + "sync" "testing" skymsg "github.com/skynetservices/skydns/msg" @@ -26,16 +28,12 @@ import ( "github.com/stretchr/testify/require" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/client/cache" - "net" ) const ( - testDomain = "cluster.local." - basePath = "/skydns/local/cluster" - serviceSubDomain = "svc" - podSubDomain = "pod" - testService = "testservice" - testNamespace = "default" + testDomain = "cluster.local." + testService = "testservice" + testNamespace = "default" ) func newKubeDNS() *KubeDNS { @@ -44,6 +42,7 @@ func newKubeDNS() *KubeDNS { endpointsStore: cache.NewStore(cache.MetaNamespaceKeyFunc), servicesStore: cache.NewStore(cache.MetaNamespaceKeyFunc), cache: NewTreeCache(), + cacheLock: sync.RWMutex{}, domainPath: reverseArray(strings.Split(strings.TrimRight(testDomain, "."), ".")), } return kd @@ -53,7 +52,6 @@ func TestPodDns(t *testing.T) { const ( testPodIP = "1.2.3.4" sanitizedPodIP = "1-2-3-4" - testPodName = "testPod" ) kd := newKubeDNS() @@ -240,9 +238,7 @@ func newEndpoints(service *kapi.Service, subsets ...kapi.EndpointSubset) *kapi.E Subsets: []kapi.EndpointSubset{}, } - for _, subset := range subsets { - endpoints.Subsets = append(endpoints.Subsets, subset) - } + endpoints.Subsets = append(endpoints.Subsets, subsets...) return &endpoints } @@ -310,7 +306,7 @@ func assertCNameRecordsMatchEndpointIPs(t *testing.T, kd *KubeDNS, e []kapi.Endp assert.Equal(t, len(e), len(records), "unexpected record count") for _, record := range records { _, found := endpoints[getIPForCName(t, kd, record.Host)] - assert.True(t, found, "Did not endpoint with address:%s", record.Host) + assert.True(t, found, "Did not find endpoint with address:%s", record.Host) } } @@ -342,20 +338,18 @@ func assertNoSRVForNamedPort(t *testing.T, kd *KubeDNS, s *kapi.Service, portNam } func assertNoDNSForClusterIP(t *testing.T, kd *KubeDNS, s *kapi.Service) { - records, err := kd.Records(getServiceFQDN(kd, s), false) - require.Error(t, err) - assert.Equal(t, 0, len(records)) + serviceFQDN := getServiceFQDN(kd, s) + queries := getEquivalentQueries(serviceFQDN, s.Namespace) + for _, query := range queries { + records, err := kd.Records(query, false) + require.Error(t, err) + assert.Equal(t, 0, len(records)) + } } func assertDNSForClusterIP(t *testing.T, kd *KubeDNS, s *kapi.Service) { serviceFQDN := getServiceFQDN(kd, s) - queries := []string{ - serviceFQDN, - strings.Replace(serviceFQDN, ".svc.", ".*.", 1), - strings.Replace(serviceFQDN, s.Namespace, "*", 1), - strings.Replace(strings.Replace(serviceFQDN, s.Namespace, "*", 1), ".svc.", ".*.", 1), - "*." + serviceFQDN, - } + queries := getEquivalentQueries(serviceFQDN, s.Namespace) for _, query := range queries { records, err := kd.Records(query, false) require.NoError(t, err) @@ -364,12 +358,22 @@ func assertDNSForClusterIP(t *testing.T, kd *KubeDNS, s *kapi.Service) { } } +func getEquivalentQueries(serviceFQDN, namespace string) []string { + return []string{ + serviceFQDN, + strings.Replace(serviceFQDN, ".svc.", ".*.", 1), + strings.Replace(serviceFQDN, namespace, "*", 1), + strings.Replace(strings.Replace(serviceFQDN, namespace, "*", 1), ".svc.", ".*.", 1), + "*." + serviceFQDN, + } +} + func getServiceFQDN(kd *KubeDNS, s *kapi.Service) string { return fmt.Sprintf("%s.%s.svc.%s", s.Name, s.Namespace, kd.domain) } func getEndpointsFQDN(kd *KubeDNS, e *kapi.Endpoints) string { - return fmt.Sprintf("%s.%s.svc.%s", e.ObjectMeta.Name, e.ObjectMeta.Namespace, kd.domain) + return fmt.Sprintf("%s.%s.svc.%s", e.Name, e.Namespace, kd.domain) } func getSRVFQDN(kd *KubeDNS, s *kapi.Service, portName string) string { diff --git a/pkg/dns/treecache.go b/pkg/dns/treecache.go index ee3ba206ee7..8403f445415 100644 --- a/pkg/dns/treecache.go +++ b/pkg/dns/treecache.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,136 +18,65 @@ package dns import ( "bytes" - "crypto/md5" "encoding/json" - "fmt" - "io/ioutil" - "os" - "path" - "reflect" "strings" - "sync" ) -const ( - dataFile = "data.dat" - crcFile = "data.crc" -) - -type object interface{} - type TreeCache struct { ChildNodes map[string]*TreeCache Entries map[string]interface{} - m *sync.RWMutex } func NewTreeCache() *TreeCache { return &TreeCache{ ChildNodes: make(map[string]*TreeCache), Entries: make(map[string]interface{}), - m: &sync.RWMutex{}, } } -func Deserialize(dir string) (*TreeCache, error) { - b, err := ioutil.ReadFile(path.Join(dir, dataFile)) - if err != nil { - return nil, err - } - var hash []byte - hash, err = ioutil.ReadFile(path.Join(dir, crcFile)) - if err != nil { - return nil, err - } - if !reflect.DeepEqual(hash, getMD5(b)) { - return nil, fmt.Errorf("Checksum failed") - } - - var cache TreeCache - err = json.Unmarshal(b, &cache) - if err != nil { - return nil, err - } - cache.m = &sync.RWMutex{} - return &cache, nil -} - -func (cache *TreeCache) Serialize(dir string) (string, error) { - cache.m.RLock() - defer cache.m.RUnlock() +func (cache *TreeCache) Serialize() (string, error) { b, err := json.Marshal(cache) if err != nil { return "", err } - if len(dir) == 0 { - var prettyJSON bytes.Buffer - err = json.Indent(&prettyJSON, b, "", "\t") + var prettyJSON bytes.Buffer + err = json.Indent(&prettyJSON, b, "", "\t") - if err != nil { - return "", err - } - return string(prettyJSON.Bytes()), nil - } - if err := ensureDir(dir, os.FileMode(0755)); err != nil { + if err != nil { return "", err } - if err := ioutil.WriteFile(path.Join(dir, dataFile), b, 0644); err != nil { - return "", err - } - if err := ioutil.WriteFile(path.Join(dir, crcFile), getMD5(b), 0644); err != nil { - return "", err - } - return string(b), nil + return string(prettyJSON.Bytes()), nil } -func (cache *TreeCache) SetEntry(key string, val interface{}, path ...string) { - cache.m.Lock() - defer cache.m.Unlock() +func (cache *TreeCache) setEntry(key string, val interface{}, path ...string) { node := cache.ensureChildNode(path...) node.Entries[key] = val } -func (cache *TreeCache) ReplaceEntries(entries map[string]interface{}, path ...string) { - cache.m.Lock() - defer cache.m.Unlock() - node := cache.ensureChildNode(path...) - node.Entries = make(map[string]interface{}) - for key, val := range entries { - node.Entries[key] = val - } -} - -func (cache *TreeCache) GetSubCache(path ...string) *TreeCache { +func (cache *TreeCache) getSubCache(path ...string) *TreeCache { childCache := cache for _, subpath := range path { childCache = childCache.ChildNodes[subpath] if childCache == nil { - return childCache + return nil } } return childCache } -func (cache *TreeCache) SetSubCache(key string, subCache *TreeCache, path ...string) { - cache.m.Lock() - defer cache.m.Unlock() +func (cache *TreeCache) setSubCache(key string, subCache *TreeCache, path ...string) { node := cache.ensureChildNode(path...) node.ChildNodes[key] = subCache } -func (cache *TreeCache) GetEntry(key string, path ...string) (interface{}, bool) { - cache.m.RLock() - defer cache.m.RUnlock() - childNode := cache.GetSubCache(path...) +func (cache *TreeCache) getEntry(key string, path ...string) (interface{}, bool) { + childNode := cache.getSubCache(path...) val, ok := childNode.Entries[key] return val, ok } -func (cache *TreeCache) GetValuesForPathWithRegex(path ...string) []interface{} { - cache.m.RLock() - defer cache.m.RUnlock() +func (cache *TreeCache) getValuesForPathWithWildcards(path ...string) []interface{} { retval := []interface{}{} nodesToExplore := []*TreeCache{cache} for idx, subpath := range path { @@ -155,7 +84,7 @@ func (cache *TreeCache) GetValuesForPathWithRegex(path ...string) []interface{} if idx == len(path)-1 { // if path ends on an entry, instead of a child node, add the entry for _, node := range nodesToExplore { - if subpath == "*" || subpath == "any" { + if subpath == "*" { nextNodesToExplore = append(nextNodesToExplore, node) } else { if val, ok := node.Entries[subpath]; ok { @@ -172,7 +101,7 @@ func (cache *TreeCache) GetValuesForPathWithRegex(path ...string) []interface{} break } - if subpath == "*" || subpath == "any" { + if subpath == "*" { for _, node := range nodesToExplore { for subkey, subnode := range node.ChildNodes { if !strings.HasPrefix(subkey, "_") { @@ -200,26 +129,11 @@ func (cache *TreeCache) GetValuesForPathWithRegex(path ...string) []interface{} return retval } -func (cache *TreeCache) GetEntries(recursive bool, path ...string) []interface{} { - cache.m.RLock() - defer cache.m.RUnlock() - childNode := cache.GetSubCache(path...) - if childNode == nil { - return nil - } - - retval := [][]interface{}{{}} - childNode.appendValues(recursive, retval) - return retval[0] -} - -func (cache *TreeCache) DeletePath(path ...string) bool { +func (cache *TreeCache) deletePath(path ...string) bool { if len(path) == 0 { return false } - cache.m.Lock() - defer cache.m.Unlock() - if parentNode := cache.GetSubCache(path[:len(path)-1]...); parentNode != nil { + if parentNode := cache.getSubCache(path[:len(path)-1]...); parentNode != nil { if _, ok := parentNode.ChildNodes[path[len(path)-1]]; ok { delete(parentNode.ChildNodes, path[len(path)-1]) return true @@ -228,10 +142,8 @@ func (cache *TreeCache) DeletePath(path ...string) bool { return false } -func (tn *TreeCache) DeleteEntry(key string, path ...string) bool { - tn.m.Lock() - defer tn.m.Unlock() - childNode := tn.GetSubCache(path...) +func (cache *TreeCache) deleteEntry(key string, path ...string) bool { + childNode := cache.getSubCache(path...) if childNode == nil { return false } @@ -242,22 +154,22 @@ func (tn *TreeCache) DeleteEntry(key string, path ...string) bool { return false } -func (tn *TreeCache) appendValues(recursive bool, ref [][]interface{}) { - for _, value := range tn.Entries { +func (cache *TreeCache) appendValues(recursive bool, ref [][]interface{}) { + for _, value := range cache.Entries { ref[0] = append(ref[0], value) } if recursive { - for _, node := range tn.ChildNodes { + for _, node := range cache.ChildNodes { node.appendValues(recursive, ref) } } } -func (tn *TreeCache) ensureChildNode(path ...string) *TreeCache { - childNode := tn +func (cache *TreeCache) ensureChildNode(path ...string) *TreeCache { + childNode := cache for _, subpath := range path { - newNode := childNode.ChildNodes[subpath] - if newNode == nil { + newNode, ok := childNode.ChildNodes[subpath] + if !ok { newNode = NewTreeCache() childNode.ChildNodes[subpath] = newNode } @@ -266,47 +178,70 @@ func (tn *TreeCache) ensureChildNode(path ...string) *TreeCache { return childNode } -func ensureDir(path string, perm os.FileMode) error { - s, err := os.Stat(path) - if err != nil || !s.IsDir() { - return os.Mkdir(path, perm) - } - return nil -} +// unused function. keeping it around in commented-fashion +// in the future, we might need some form of this function so that +// we can serialize to a file in a mounted empty dir.. +//const ( +// dataFile = "data.dat" +// crcFile = "data.crc" +//) +//func (cache *TreeCache) Serialize(dir string) (string, error) { +// cache.m.RLock() +// defer cache.m.RUnlock() +// b, err := json.Marshal(cache) +// if err != nil { +// return "", err +// } +// +// if err := ensureDir(dir, os.FileMode(0755)); err != nil { +// return "", err +// } +// if err := ioutil.WriteFile(path.Join(dir, dataFile), b, 0644); err != nil { +// return "", err +// } +// if err := ioutil.WriteFile(path.Join(dir, crcFile), getMD5(b), 0644); err != nil { +// return "", err +// } +// return string(b), nil +//} -func getMD5(b []byte) []byte { - h := md5.New() - h.Write(b) - return []byte(fmt.Sprintf("%x", h.Sum(nil))) -} +//func ensureDir(path string, perm os.FileMode) error { +// s, err := os.Stat(path) +// if err != nil || !s.IsDir() { +// return os.Mkdir(path, perm) +// } +// return nil +//} -func main() { - root := NewTreeCache() - fmt.Println("Adding Entries") - root.SetEntry("k", "v") - root.SetEntry("foo", "bar", "local") - root.SetEntry("foo1", "bar1", "local", "cluster") +//func getMD5(b []byte) []byte { +// h := md5.New() +// h.Write(b) +// return []byte(fmt.Sprintf("%x", h.Sum(nil))) +//} - fmt.Println("Fetching Entries") - for _, entry := range root.GetEntries(true, "local") { - fmt.Printf("%s\n", entry) - } - - fmt.Println("Serializing") - if _, err := root.Serialize("./foo"); err != nil { - fmt.Printf("Serialization Error: %v,\n", err) - return - } - - fmt.Println("Deserializing") - tn, err := Deserialize("./foo") - if err != nil { - fmt.Printf("Deserialization Error: %v\n", err) - return - } - - fmt.Println("Fetching Entries") - for _, entry := range tn.GetEntries(true, "local") { - fmt.Printf("%s\n", entry) - } -} +// unused function. keeping it around in commented-fashion +// in the future, we might need some form of this function so that +// we can restart kube-dns, deserialize the tree and have a cache +// without having to wait for kube-dns to reach out to API server. +//func Deserialize(dir string) (*TreeCache, error) { +// b, err := ioutil.ReadFile(path.Join(dir, dataFile)) +// if err != nil { +// return nil, err +// } +// +// hash, err := ioutil.ReadFile(path.Join(dir, crcFile)) +// if err != nil { +// return nil, err +// } +// if !reflect.DeepEqual(hash, getMD5(b)) { +// return nil, fmt.Errorf("Checksum failed") +// } +// +// var cache TreeCache +// err = json.Unmarshal(b, &cache) +// if err != nil { +// return nil, err +// } +// cache.m = &sync.RWMutex{} +// return &cache, nil +//}