// Copyright 2011 Miek Gieben. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package dns import ( "os" "strings" ) const _CLASS = 2 << 16 // ZRRset is a structure that keeps several items from // a zone file together. It helps in speeding up DNSSEC lookups. type ZRRset struct { RRs RRset // the RRset for this type and name RRsigs RRset // the RRSIGs belonging to this RRset (if any) Nxt RR // the NSEC or NSEC3 for this name Glue bool // when this RRset is glue, set to true } // For DNSSEC I also need to know the PREVIOUS name for this RRSET...Or at least the // previous NXT RR func NewZRRset() *ZRRset { s := new(ZRRset) s.RRs = NewRRset() s.RRsigs = NewRRset() return s } // Zone implements the concept of RFC 1035 master zone files. // This will be converted to some kind of tree structure type Zone map[string]map[int]*ZRRset func NewZone() Zone { z := make(Zone) return z } // Get the first value func (z Zone) Pop() *ZRRset { for _, v := range z { for _, v1 := range v { return v1 } } return nil } func (z Zone) PopRR() RR { s := z.Pop() if s == nil { return nil } switch { case len(s.RRs) != 0: return s.RRs.Pop() case len(s.RRsigs) != 0: return s.RRsigs.Pop() case s.Nxt != nil: return s.Nxt } panic("not reached") return nil } func (z Zone) Len() int { i := 0 for _, im := range z { for _, s := range im { i += len(s.RRs) + len(s.RRsigs) if s.Nxt != nil { i++ } } } return i } func (z Zone) String() string { s := "" for _, im := range z { for _, s1 := range im { s += s1.RRs.String() s += s1.RRsigs.String() if s1.Nxt != nil { s += s1.Nxt.String() + "\n" } } } return s } // Add a new RR to the zone. First we need to find out if the // RR already lives inside it. func (z Zone) PushRR(r RR) { s, _ := z.LookupRR(r) if s == nil { s = NewZRRset() } switch r.Header().Rrtype { case TypeRRSIG: s.RRsigs.Push(r) case TypeNSEC, TypeNSEC3: s.Nxt = r default: s.RRs.Push(r) } z.Push(s) } // Push a new ZRRset to the zone func (z Zone) Push(s *ZRRset) { // s can hold RRs, RRsigs or a Nxt name := "" i := 0 switch { case len(s.RRs) != 0: name = s.RRs[0].Header().Name i = intval(s.RRs[0].Header().Class, s.RRs[0].Header().Rrtype) case len(s.RRsigs) != 0: name = s.RRsigs[0].Header().Name i = intval(s.RRsigs[0].Header().Class, s.RRsigs[0].(*RR_RRSIG).TypeCovered) case s.Nxt != nil: name = s.Nxt.Header().Name i = intval(s.Nxt.Header().Class, s.Nxt.Header().Rrtype) } if z[name] == nil { im := make(map[int]*ZRRset) // intmap im[i] = s z[name] = im return } im := z[name] im[i] = s return } // Lookup the RR in the zone, we are only looking at // qname, qtype and qclass of the RR // Considerations for wildcards // Return NXDomain, Name error, wildcard? // Casing! func (z Zone) LookupRR(r RR) (*ZRRset, os.Error) { if r.Header().Rrtype == TypeRRSIG { return z.LookupName(r.Header().Name, r.Header().Class, r.(*RR_RRSIG).TypeCovered) } return z.LookupName(r.Header().Name, r.Header().Class, r.Header().Rrtype) } func (z Zone) LookupQuestion(q Question) (*ZRRset, os.Error) { // Impossible to look for an typecovered in a question, because the rdata is // not there. return z.LookupName(q.Name, q.Qclass, q.Qtype) } func (z Zone) LookupName(qname string, qclass, qtype uint16) (*ZRRset, os.Error) { i := intval(qclass, qtype) if im, ok := z[strings.ToLower(qname)]; ok { // Have an im, intmap if s, ok := im[i]; ok { return s, nil } // Wildcard 'n stuff return nil, ErrName } return nil, nil } // Number in the second map denotes the class + type. func intval(c, t uint16) int { return int(c)*_CLASS + int(t) } // Needed for NSEC/NSEC3 in DNSSEC // SortInsert insert the string s in the already sorted // vector p. If s is already present it is not inserted again. func SortInsert(p *vector.StringVector, s string) { sa := sort.StringArray(*p) i := sa.Search(s) if i < p.Len() && p.At(i) == s { // element already there return } p.Insert(i, s) } // Search searches the sorted vector p using binary search. If // the element s can not be found, the previous element is returned. func SortSearch(p *vector.StringVector, s string) string { sa := sort.StringArray(*p) i := sa.Search(s) // with zones there must always be one before if p.At(i) == s { return s } if i > 0 { i-- } return p.At(i) }