Пример #1
0
// 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)
}
Пример #2
0
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
}
Пример #3
0
// 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
}