// Our shiny lookup loop func (cq *Cq) collapsedLookup(q packet.QuestionFormat, c chan *lookupRes) { for i := 0; i < 5; { cres, cerr := cq.cache.Lookup(q.Name, q.Type) if cres != nil { c <- &lookupRes{cres, false} break } if cerr != nil { c <- &lookupRes{cerr, true} break } // No such type exists in cache, but maybe we got a CNAME... horray. cres, _ = cq.cache.Lookup(q.Name, constants.TYPE_CNAME) if cres != nil { if len(cres.ResourceRecord) == 1 { // We do not support weird multi-record cnames target_label, err := packet.ParseName(cres.ResourceRecord[0].Data) if err == nil { // Restart query with cname label but inherit types of original query. target_chan := make(chan *lookupRes) target_q := packet.QuestionFormat{Name: target_label, Type: q.Type, Class: q.Class} go cq.collapsedLookup(target_q, target_chan) target_res := <-target_chan if target_res != nil { // not a dead cname: we got the requested record -> append it to original cache reply cres.ResourceRecord = append(cres.ResourceRecord, target_res.cres.ResourceRecord...) } c <- &lookupRes{cres, false} } } break } pp := cq.advanceCache(q) progress := cq.blockForQuery(pp) if progress == false { i++ } } // return pseudio-nil if we give up close(c) }
func (cq *Cq) advanceCache(q packet.QuestionFormat) *packet.ParsedPacket { // our hardcoded, not so redundant slist targetNS := "192.5.5.241:53" targetXH := &packet.Namelabel{} targetQT := q.Type POP_LOOP: for i := 0; ; i++ { label := q.Name.PoppedLabel(i) // removes 'i' labels from the label list nsrec, _ := cq.cache.Lookup(*label, constants.TYPE_NS) if nsrec != nil { // we got an NS cache entry for this level var candidate_cres *cache.CacheResult var candidate_label packet.Namelabel // loop trough all NS servers for this record for _, candidate_data := range nsrec.ResourceRecord { name, err := packet.ParseName(candidate_data.Data) if err == nil { l.Debug("NS %v handles %v", name, label) cres, _ := cq.cache.Lookup(name, constants.TYPE_A) if cres != nil { candidate_cres = cres } else { candidate_label = name } } } // Fixme: We should try to resolve (yet unknown) nameservers // even if we got a candidate_res as the one we are contacting // might fail for some reason. if candidate_cres == nil && candidate_label.Len() > 0 { l.Debug("Looking up IP of known candidate: %v", candidate_label) c := make(chan *lookupRes) go cq.collapsedLookup(packet.QuestionFormat{Type: constants.TYPE_A, Class: constants.CLASS_IN, Name: candidate_label}, c) lres := <-c if lres != nil && lres.negative == false { candidate_cres = lres.cres } } if candidate_cres != nil { l.Debug("We got an RR: %v", candidate_cres) for _, v := range candidate_cres.ResourceRecord { if v.Type != constants.TYPE_A { l.Panic("Not an A type: %v", v) } targetNS = fmt.Sprintf("%d.%d.%d.%d:53", v.Data[0], v.Data[1], v.Data[2], v.Data[3]) targetXH = label break POP_LOOP } } } if label.Len() == 1 { break } } pp := &packet.ParsedPacket{} pp.Header.Id = uint16(rand.Uint32()) // will simply overflow pp.Header.Opcode = constants.OP_QUERY pp.Header.QuestionCount = 1 pp.Questions = []packet.QuestionFormat{{Name: q.Name, Class: constants.CLASS_IN, Type: targetQT}} remoteNs, err := net.ResolveUDPAddr("udp", targetNS) if err == nil { l.Info("+ op=query, remote=%s, type=%d, id=%d, name=%v", targetNS, targetQT, pp.Header.Id, q.Name) cq.conn.WriteToUDP(packet.Assemble(pp), remoteNs) cq.sq.registerQuery(pp.Questions[0], remoteNs, targetXH) } return pp }
// Lookup returns the CacheResult of given Namelabel and Type combination // rr will be nil if there was no positive match // re will be nil if there was no negative match // rr == re == nil if the entry is completely unknown func (c *Cache) Lookup(label packet.Namelabel, t uint16) (rr *CacheResult, re *CacheResult) { key := label.ToKey() now := time.Now() c.Lock() defer c.Unlock() if c.CacheMap[key] != nil { qtypes := []uint16{t} // the types we are going to query from the cache ent := make([]packet.ResourceRecordFormat, 0) // the final response if t == constants.QTYPE_ALL && len(c.CacheMap[key]) > 0 { // special case: This was an ANY query and we DO have some data. // As QTYPE_ALL is not a valid type, we are just going to return all rr's we got data for qtypes = []uint16{} for k, _ := range c.CacheMap[key] { qtypes = append(qtypes, k) } } for _, qtype := range qtypes { if c.CacheMap[key][qtype] != nil { for _, item := range c.CacheMap[key][qtype] { if now.Before(item.deadline) { ttl := uint32(item.deadline.Sub(now).Seconds()) ent = append(ent, packet.ResourceRecordFormat{Name: label, Class: constants.CLASS_IN, Type: qtype, Ttl: ttl, Data: item.data}) } } } } if len(ent) > 0 { // ensure to return a null pointer if ent is empty rr = &CacheResult{ResourceRecord: ent, ResponseCode: constants.RC_NO_ERR} } } // rr will be nil on cache miss, check if we have a negative cache entry if rr == nil && c.MissMap[key] != nil { mtype := t if c.MissMap[key][constants.TYPE_SOA] != nil { // we do have a negative soa entry, so the domain simply does not exist // and there is no point in looking up 't' mtype = constants.TYPE_SOA } if c.MissMap[key][mtype] != nil { for _, item := range c.MissMap[key][mtype] { if now.Before(item.deadline) { ttl := uint32(item.deadline.Sub(now).Seconds()) // unparse fiddled-in soa label rend := item.data[0] + 1 rlabel := item.data[1:rend] plabel, _ := packet.ParseName(rlabel) ent := make([]packet.ResourceRecordFormat, 0) ent = append(ent, packet.ResourceRecordFormat{Name: plabel, Class: constants.CLASS_IN, Type: constants.TYPE_SOA, Ttl: ttl, Data: item.data[rend:]}) re = &CacheResult{ResourceRecord: ent, ResponseCode: item.rcode} } } } } return }