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") } }
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) } } }
// 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) } } } }
// 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) }
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 }