Example #1
0
func (cq *Cq) clientLookup(cr *clientRequest) {
	// Ensure that this query makes some sense
	if len(cr.Query.Questions) == 1 {
		q := cr.Query.Questions[0]
		c := make(chan *lookupRes)
		go cq.collapsedLookup(q, c)
		lres := <-c
		l.Debug("final lookup reply -> %v", lres)
		if lres != nil { // fixme: error
			cres := lres.cres
			p := &packet.ParsedPacket{}
			p.Header.Id = cr.Query.Header.Id
			p.Header.Response = true
			p.Header.ResponseCode = cres.ResponseCode
			p.Questions = cr.Query.Questions
			if lres.negative {
				p.Nameservers = append(p.Nameservers, cres.ResourceRecord...)
			} else {
				p.Answers = append(p.Answers, cres.ResourceRecord...)
			}
			cq.conn.WriteToUDP(packet.Assemble(p), cr.RemoteAddr)
		} else {
			l.Info("Lookup returned an error, should send it back to client (fixme): %+v", lres)
		}
	} else {
		l.Info("Dropping nonsense query")
	}
}
Example #2
0
func main() {
	flag.Parse()

	listenStr := fmt.Sprintf(":%d", *listenPort)
	l.Info("Starting up, listening on %s", listenStr)

	listenAddr, err := net.ResolveUDPAddr("udp", listenStr)
	if err != nil {
		l.Panic("ResolveUDPAddr failed: %v", err)
	}

	conn, err := net.ListenUDP("udp", listenAddr)
	if err != nil {
		l.Panic("listen failed: %v", err)
	}

	buf := make([]byte, constants.MAX_SIZE_UDP) // Upper limit as defined by RFC 1035 2.3.4
	nc := cache.NewNameCache()
	sq := queue.NewServerQueue(nc)
	cq := queue.NewClientQueue(conn, nc, sq)

	for {
		nread, remoteAddr, err := conn.ReadFromUDP(buf)
		if err != nil || nread < constants.FIX_SIZE_HEADER {
			l.Debug("%v dropping malformed datagram. Size=%d, err=%v", remoteAddr, nread, err)
			continue
		}

		p, err := packet.Parse(buf[0:nread])
		if err != nil {
			l.Debug("%v failed to parse datagram, err=%v", remoteAddr, err)
			continue
		}

		if p.Header.Response == false && p.Header.Opcode == constants.OP_QUERY && p.Header.RecDesired {
			// This is a query, requesting recursion
			cq.AddClientRequest(p, remoteAddr)
		} else if p.Header.Response == true && p.Header.Opcode == constants.OP_QUERY {
			// A reply, try to put it into our cache
			nc.Put(p, remoteAddr)
		} else {
			// DOES NOT COMPUTE.
			l.Info("%v dropped packet", remoteAddr)
		}

	}
}
Example #3
0
// Puts given entry into c's Cache
func (c *Cache) Put(p *packet.ParsedPacket, ns *net.UDPAddr) {
	if len(p.Questions) != 1 {
		return
	}

	// Check if we have a CrossHierarchy (XH) label
	// a nil value indicates that this reply is invalid
	xhlabel := c.VrfyCallback(p.Questions[0], ns)
	if xhlabel == nil {
		l.Info("Dropping unexpected reply from %v", ns)
		return
	}

	qname := p.Questions[0].Name
	qtype := p.Questions[0].Type
	isrc := InjectSource{Name: qname, Type: qtype}

	if p.Header.Authoritative == true {
		for _, n := range p.Answers {
			if n.Class == constants.CLASS_IN && n.Name.IsChildOf(xhlabel) {
				c.injectPositiveItem(isrc, n)
			}
		}
	}

	// Scan if there any additional A or AAA records
	for _, n := range p.Additionals {
		if n.Class == constants.CLASS_IN && n.Name.IsChildOf(xhlabel) {
			if n.Type == constants.TYPE_A || n.Type == constants.TYPE_AAAA {
				c.injectPositiveItem(isrc, n)
			}
		}
	}

	for _, n := range p.Nameservers {
		if n.Class == constants.CLASS_IN && n.Name.IsChildOf(xhlabel) {
			if n.Type == constants.TYPE_NS {
				c.injectPositiveItem(isrc, n)
			}
			if p.Header.AnswerCount == 0 && n.Type == constants.TYPE_SOA {
				c.injectNegativeItem(isrc, n, p.Header.ResponseCode)
			}
		}
	}
}
Example #4
0
// injectNegativeItem marks given label as non existing. rc defines the return code
// item is supposed to be a SOA
func (c *Cache) injectNegativeItem(isrc InjectSource, item packet.ResourceRecordFormat, rcode uint8) {
	if item.Type != constants.TYPE_SOA {
		panic("Not a SOA!")
	}

	// Unbound uses the RR TTL while some other caches seem to use SOA.MINTTL ?
	// We are going unbound-style but emit a warning
	soaTtl := packet.ParseSoaTtl(item.Data)
	if item.Ttl != soaTtl {
		l.Info("SOA ttl mismatch: %d != %d (isrc=%+v)", item.Ttl, soaTtl, isrc)
	}

	switch {
	case item.Ttl < 5:
		item.Ttl = 5
	case item.Ttl > 600:
		item.Ttl = 600
	}

	// xxx: The cache key for this entry should not be the response label but the
	// question (isrc) label. However: The response label needs to be preserved
	// so we are prefixing it to the raw data for now (until we have a nicer API)
	rawlabel := packet.EncodeName(item.Name)
	item.Data = append(rawlabel, item.Data...)
	item.Data = append([]byte{byte(len(rawlabel))}, item.Data...)
	item.Name = isrc.Name // use the looked up label as cache key, not the SOA label

	// We shall also use the source TYPE *unless* we are storing an NXDOMAIN entry
	if rcode != constants.RC_NAME_ERR {
		item.Type = isrc.Type
	} else {
		// this was an NXDOMAIN -> a negative SOA entry signals that NO RRs exist
		item.Type = constants.TYPE_SOA
	}

	c.injectInternal(c.MissMap, isrc, item, rcode)
}
Example #5
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
}