コード例 #1
0
ファイル: forwarding.go プロジェクト: janeczku/go-dnsmasq
// forwardSearch resolves a query by suffixing with search paths
func (s *server) forwardSearch(req *dns.Msg, tcp bool) (*dns.Msg, error) {
	var r *dns.Msg
	var nodata *dns.Msg   // stores the copy of a NODATA reply
	var searchName string // stores the current name suffixed with search domain
	var err error
	var didSearch bool
	name := req.Question[0].Name // original qname
	reqCopy := req.Copy()

	for _, domain := range s.config.SearchDomains {
		if strings.HasSuffix(name, domain) {
			continue
		}

		searchName = strings.ToLower(appendDomain(name, domain))
		reqCopy.Question[0] = dns.Question{searchName, reqCopy.Question[0].Qtype, reqCopy.Question[0].Qclass}
		didSearch = true
		r, err = s.forwardQuery(reqCopy, tcp)
		if err != nil {
			// No server currently available, give up
			break
		}

		switch r.Rcode {
		case dns.RcodeSuccess:
			// In case of NO_DATA keep searching, otherwise a wildcard entry
			// could keep us from finding the answer higher in the search list
			if len(r.Answer) == 0 && !r.MsgHdr.Truncated {
				nodata = r.Copy()
				continue
			}
		case dns.RcodeNameError:
			fallthrough
		case dns.RcodeServerFailure:
			// try next search element if any
			continue
		}
		// anything else implies that we are done searching
		break
	}

	if !didSearch {
		m := new(dns.Msg)
		m.SetRcode(req, dns.RcodeNameError)
		return m, nil
	}

	if err == nil {
		if r.Rcode == dns.RcodeSuccess {
			if len(r.Answer) > 0 {
				cname := new(dns.CNAME)
				cname.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 360}
				cname.Target = searchName
				answers := []dns.RR{cname}
				for _, rr := range r.Answer {
					answers = append(answers, rr)
				}
				r.Answer = answers
			}
			// If we ever got a NODATA, return this instead of a negative result
		} else if nodata != nil {
			r = nodata
		}
		// Restore original question
		r.Question[0] = req.Question[0]
	}

	if err != nil && nodata != nil {
		r = nodata
		r.Question[0] = req.Question[0]
		err = nil
	}

	return r, err
}
コード例 #2
0
ファイル: main.go プロジェクト: axw/jns
// TODO(axw) define an error for "no answer"
func (s *jujuNameServer) answer(q dns.Question) (dns.RR, error) {
	if q.Qtype != dns.TypeA {
		return nil, nil
	}

	var envName string
	entityName := strings.ToLower(strings.TrimSuffix(q.Name, "."+zone))
	if i := strings.IndexRune(entityName, '.'); i >= 0 {
		envName = entityName[i+1:]
		entityName = entityName[:i]
	} else {
		var err error
		envName, err = envcmd.GetDefaultEnvironment()
		if err != nil {
			return nil, err
		}
	}

	// TODO(axw) cache API connection
	api, err := s.openAPI(envName)
	if err != nil {
		return nil, err
	}
	defer api.Close()
	client := api.Client()

	// If the entity name parses as a tag, extract the ID. This enables
	// us to address "unit-mysql-0", where we couldn't otherwise, since
	// slashes are not allowed in domain names. Similarly for container
	// machines (e.g. to address "0/lxc/0", pass "machine-0-lxc-0").
	if tag, err := names.ParseTag(entityName); err == nil {
		entityName = tag.Id()
	}

	var addr string
	if names.IsValidService(entityName) {
		status, err := client.Status([]string{entityName})
		if err != nil {
			return nil, err
		}
		service := status.Services[entityName]
		addresses := make([]string, 0, len(service.Units))
		for _, unit := range service.Units {
			if unit.PublicAddress == "" {
				continue
			}
			if includeLost || unit.UnitAgent.Status != "lost" {
				addresses = append(addresses, unit.PublicAddress)
			}
		}
		// Might be nice to have additional info in TXT?
		if len(addresses) == 0 {
			return nil, nil
		}
		addr = addresses[rand.Intn(len(addresses))]
	} else {
		// Assume it's a machine or unit name.
		addr, err = client.PublicAddress(entityName)
		if err != nil {
			return nil, err
		}
	}

	ip := net.ParseIP(addr)
	if ip != nil {
		rr := new(dns.A)
		rr.Hdr = dns.RR_Header{
			Name:   q.Name,
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    0,
		}
		rr.A = ip
		return rr, nil
	} else {
		rr := new(dns.CNAME)
		rr.Hdr = dns.RR_Header{
			Name:   q.Name,
			Rrtype: dns.TypeCNAME,
			Class:  dns.ClassINET,
			Ttl:    0,
		}
		rr.Target = addr + "."
		return rr, nil
	}
}