示例#1
0
文件: resolver.go 项目: vmware/vic
func (r *resolver) forwardQueryStart(w dns.ResponseWriter, msg *dns.Msg, queryID uint16) bool {
	proto := w.LocalAddr().Network()
	dnsID := uint16(rand.Intn(maxDNSID))

	cc := clientConn{
		dnsID:      queryID,
		respWriter: w,
	}

	r.queryLock.Lock()
	defer r.queryLock.Unlock()

	if r.count == maxConcurrent {
		return false
	}
	r.count++

	switch proto {
	case "tcp":
		break
	case "udp":
		for ok := true; ok == true; dnsID = uint16(rand.Intn(maxDNSID)) {
			_, ok = r.client[dnsID]
		}
		log.Debugf("client dns id %v, changed id %v", queryID, dnsID)
		r.client[dnsID] = cc
		msg.Id = dnsID
	default:
		log.Errorf("Invalid protocol..")
		return false
	}

	return true
}
示例#2
0
文件: resolver.go 项目: vmware/vic
func (r *resolver) forwardQueryEnd(w dns.ResponseWriter, msg *dns.Msg) dns.ResponseWriter {
	var (
		cc clientConn
		ok bool
	)
	proto := w.LocalAddr().Network()

	r.queryLock.Lock()
	defer r.queryLock.Unlock()

	if r.count == 0 {
		log.Errorf("Invalid concurrent query count")
	} else {
		r.count--
	}

	switch proto {
	case "tcp":
		break
	case "udp":
		if cc, ok = r.client[msg.Id]; ok == false {
			log.Debugf("Can't retrieve client context for dns id %v", msg.Id)
			return nil
		}
		log.Debugf("dns msg id %v, client id %v", msg.Id, cc.dnsID)
		delete(r.client, msg.Id)
		msg.Id = cc.dnsID
		w = cc.respWriter
	default:
		log.Errorf("Invalid protocol")
		return nil
	}
	return w
}
示例#3
0
文件: resolver.go 项目: contiv/docker
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		resp *dns.Msg
		err  error
	)

	if query == nil || len(query.Question) == 0 {
		return
	}
	name := query.Question[0].Name
	if query.Question[0].Qtype == dns.TypeA {
		resp, err = r.handleIPv4Query(name, query)
	} else if query.Question[0].Qtype == dns.TypePTR {
		resp, err = r.handlePTRQuery(name, query)
	}

	if err != nil {
		log.Error(err)
		return
	}

	if resp == nil {
		if len(r.extDNS) == 0 {
			return
		}

		num := maxExtDNS
		if len(r.extDNS) < maxExtDNS {
			num = len(r.extDNS)
		}
		for i := 0; i < num; i++ {
			log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype)

			c := &dns.Client{Net: w.LocalAddr().Network()}
			addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)

			resp, _, err = c.Exchange(query, addr)
			if err == nil {
				resp.Compress = true
				break
			}
			log.Errorf("external resolution failed, %s", err)
		}
		if resp == nil {
			return
		}
	}

	err = w.WriteMsg(resp)
	if err != nil {
		log.Errorf("error writing resolver resp, %s", err)
	}
}
示例#4
0
func (r *resolver) checkRespInGC(w dns.ResponseWriter, msg *dns.Msg) bool {
	if w.LocalAddr().Network() != "udp" {
		return false
	}

	query := sboxQuery{
		sboxID: r.sb.ID(),
		dnsID:  msg.Id,
	}

	queryGCMutex.Lock()
	defer queryGCMutex.Unlock()
	if _, ok := queryGC[query]; ok {
		delete(queryGC, query)
		return true
	}
	return false
}
示例#5
0
func (r *resolver) addQueryToGC(w dns.ResponseWriter, msg *dns.Msg) {
	if w.LocalAddr().Network() != "udp" {
		return
	}

	r.queryLock.Lock()
	cc, ok := r.client[msg.Id]
	r.queryLock.Unlock()
	if !ok {
		return
	}

	query := sboxQuery{
		sboxID: r.sb.ID(),
		dnsID:  msg.Id,
	}
	clientGC := &clientConnGC{
		client: cc,
	}
	queryGCMutex.Lock()
	queryGC[query] = clientGC
	queryGCMutex.Unlock()
}
示例#6
0
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		extConn net.Conn
		resp    *dns.Msg
		err     error
	)

	if query == nil || len(query.Question) == 0 {
		return
	}
	name := query.Question[0].Name

	switch query.Question[0].Qtype {
	case dns.TypeA:
		resp, err = r.handleIPQuery(name, query, types.IPv4)
	case dns.TypeAAAA:
		resp, err = r.handleIPQuery(name, query, types.IPv6)
	case dns.TypePTR:
		resp, err = r.handlePTRQuery(name, query)
	case dns.TypeSRV:
		resp, err = r.handleSRVQuery(name, query)
	}

	if err != nil {
		logrus.Error(err)
		return
	}

	if resp == nil {
		// If the backend doesn't support proxying dns request
		// fail the response
		if !r.proxyDNS {
			resp = new(dns.Msg)
			resp.SetRcode(query, dns.RcodeServerFailure)
			w.WriteMsg(resp)
			return
		}

		// If the user sets ndots > 0 explicitly and the query is
		// in the root domain don't forward it out. We will return
		// failure and let the client retry with the search domain
		// attached
		switch query.Question[0].Qtype {
		case dns.TypeA:
			fallthrough
		case dns.TypeAAAA:
			if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(name, "."), ".") {
				resp = createRespMsg(query)
			}
		}
	}

	proto := w.LocalAddr().Network()
	maxSize := 0
	if proto == "tcp" {
		maxSize = dns.MaxMsgSize - 1
	} else if proto == "udp" {
		optRR := query.IsEdns0()
		if optRR != nil {
			maxSize = int(optRR.UDPSize())
		}
		if maxSize < defaultRespSize {
			maxSize = defaultRespSize
		}
	}

	if resp != nil {
		if resp.Len() > maxSize {
			truncateResp(resp, maxSize, proto == "tcp")
		}
	} else {
		for i := 0; i < maxExtDNS; i++ {
			extDNS := &r.extDNSList[i]
			if extDNS.ipStr == "" {
				break
			}
			extConnect := func() {
				addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
			}

			if extDNS.hostLoopback {
				extConnect()
			} else {
				execErr := r.backend.ExecFunc(extConnect)
				if execErr != nil {
					logrus.Warn(execErr)
					continue
				}
			}
			if err != nil {
				logrus.Warnf("Connect failed: %s", err)
				continue
			}
			logrus.Debugf("Query %s[%d] from %s, forwarding to %s:%s", name, query.Question[0].Qtype,
				extConn.LocalAddr().String(), proto, extDNS.ipStr)

			// Timeout has to be set for every IO operation.
			extConn.SetDeadline(time.Now().Add(extIOTimeout))
			co := &dns.Conn{
				Conn:    extConn,
				UDPSize: uint16(maxSize),
			}
			defer co.Close()

			// limits the number of outstanding concurrent queries.
			if r.forwardQueryStart() == false {
				old := r.tStamp
				r.tStamp = time.Now()
				if r.tStamp.Sub(old) > logInterval {
					logrus.Errorf("More than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
				}
				continue
			}

			err = co.WriteMsg(query)
			if err != nil {
				r.forwardQueryEnd()
				logrus.Debugf("Send to DNS server failed, %s", err)
				continue
			}

			resp, err = co.ReadMsg()
			// Truncated DNS replies should be sent to the client so that the
			// client can retry over TCP
			if err != nil && err != dns.ErrTruncated {
				r.forwardQueryEnd()
				logrus.Debugf("Read from DNS server failed, %s", err)
				continue
			}
			r.forwardQueryEnd()
			if resp != nil {
				for _, rr := range resp.Answer {
					h := rr.Header()
					switch h.Rrtype {
					case dns.TypeA:
						ip := rr.(*dns.A).A
						r.backend.HandleQueryResp(h.Name, ip)
					case dns.TypeAAAA:
						ip := rr.(*dns.AAAA).AAAA
						r.backend.HandleQueryResp(h.Name, ip)
					}
				}
			}
			resp.Compress = true
			break
		}
		if resp == nil {
			return
		}
	}

	if err = w.WriteMsg(resp); err != nil {
		logrus.Errorf("error writing resolver resp, %s", err)
	}
}
示例#7
0
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		extConn net.Conn
		resp    *dns.Msg
		err     error
	)

	if query == nil || len(query.Question) == 0 {
		return
	}
	name := query.Question[0].Name

	switch query.Question[0].Qtype {
	case dns.TypeA:
		resp, err = r.handleIPQuery(name, query, types.IPv4)
	case dns.TypeAAAA:
		resp, err = r.handleIPQuery(name, query, types.IPv6)
	case dns.TypePTR:
		resp, err = r.handlePTRQuery(name, query)
	case dns.TypeSRV:
		resp, err = r.handleSRVQuery(name, query)
	}

	if err != nil {
		log.Error(err)
		return
	}

	proto := w.LocalAddr().Network()
	maxSize := 0
	if proto == "tcp" {
		maxSize = dns.MaxMsgSize - 1
	} else if proto == "udp" {
		optRR := query.IsEdns0()
		if optRR != nil {
			maxSize = int(optRR.UDPSize())
		}
		if maxSize < defaultRespSize {
			maxSize = defaultRespSize
		}
	}

	if resp != nil {
		if resp.Len() > maxSize {
			truncateResp(resp, maxSize, proto == "tcp")
		}
	} else {
		for i := 0; i < maxExtDNS; i++ {
			extDNS := &r.extDNSList[i]
			if extDNS.ipStr == "" {
				break
			}
			extConnect := func() {
				addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
			}

			r.sb.execFunc(extConnect)
			if err != nil {
				log.Debugf("Connect failed, %s", err)
				continue
			}
			log.Debugf("Query %s[%d] from %s, forwarding to %s:%s", name, query.Question[0].Qtype,
				extConn.LocalAddr().String(), proto, extDNS.ipStr)

			// Timeout has to be set for every IO operation.
			extConn.SetDeadline(time.Now().Add(extIOTimeout))
			co := &dns.Conn{Conn: extConn}
			defer co.Close()

			// limits the number of outstanding concurrent queries.
			if r.forwardQueryStart() == false {
				old := r.tStamp
				r.tStamp = time.Now()
				if r.tStamp.Sub(old) > logInterval {
					log.Errorf("More than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
				}
				continue
			}

			err = co.WriteMsg(query)
			if err != nil {
				r.forwardQueryEnd()
				log.Debugf("Send to DNS server failed, %s", err)
				continue
			}

			resp, err = co.ReadMsg()
			// Truncated DNS replies should be sent to the client so that the
			// client can retry over TCP
			if err != nil && err != dns.ErrTruncated {
				r.forwardQueryEnd()
				log.Debugf("Read from DNS server failed, %s", err)
				continue
			}

			r.forwardQueryEnd()

			resp.Compress = true
			break
		}
		if resp == nil {
			return
		}
	}

	if err = w.WriteMsg(resp); err != nil {
		log.Errorf("error writing resolver resp, %s", err)
	}
}
示例#8
0
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		extConn net.Conn
		resp    *dns.Msg
		err     error
	)

	if query == nil || len(query.Question) == 0 {
		return
	}
	name := query.Question[0].Name
	if query.Question[0].Qtype == dns.TypeA {
		resp, err = r.handleIPv4Query(name, query)
	} else if query.Question[0].Qtype == dns.TypePTR {
		resp, err = r.handlePTRQuery(name, query)
	}

	if err != nil {
		log.Error(err)
		return
	}

	proto := w.LocalAddr().Network()
	maxSize := 0
	if proto == "tcp" {
		maxSize = dns.MaxMsgSize - 1
	} else if proto == "udp" {
		optRR := query.IsEdns0()
		if optRR != nil {
			maxSize = int(optRR.UDPSize())
		}
		if maxSize < defaultRespSize {
			maxSize = defaultRespSize
		}
	}

	if resp != nil {
		if resp.Len() > maxSize {
			truncateResp(resp, maxSize, proto == "tcp")
		}
	} else {
		for i := 0; i < maxExtDNS; i++ {
			extDNS := &r.extDNSList[i]
			if extDNS.ipStr == "" {
				break
			}
			log.Debugf("Querying ext dns %s:%s for %s[%d]", proto, extDNS.ipStr, name, query.Question[0].Qtype)

			extConnect := func() {
				addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
			}

			// For udp clients connection is persisted to reuse for further queries.
			// Accessing extDNS.extConn be a race here between go rouines. Hence the
			// connection setup is done in a Once block and fetch the extConn again
			extConn = extDNS.extConn
			if extConn == nil || proto == "tcp" {
				if proto == "udp" {
					extDNS.extOnce.Do(func() {
						r.sb.execFunc(extConnect)
						extDNS.extConn = extConn
					})
					extConn = extDNS.extConn
				} else {
					r.sb.execFunc(extConnect)
				}
				if err != nil {
					log.Debugf("Connect failed, %s", err)
					continue
				}
			}
			// If two go routines are executing in parralel one will
			// block on the Once.Do and in case of error connecting
			// to the external server it will end up with a nil err
			// but extConn also being nil.
			if extConn == nil {
				continue
			}

			// Timeout has to be set for every IO operation.
			extConn.SetDeadline(time.Now().Add(extIOTimeout))
			co := &dns.Conn{Conn: extConn}

			defer func() {
				if proto == "tcp" {
					co.Close()
				}
			}()
			err = co.WriteMsg(query)
			if err != nil {
				log.Debugf("Send to DNS server failed, %s", err)
				continue
			}

			resp, err = co.ReadMsg()
			if err != nil {
				log.Debugf("Read from DNS server failed, %s", err)
				continue
			}

			resp.Compress = true
			break
		}

		if resp == nil {
			return
		}
	}

	err = w.WriteMsg(resp)
	if err != nil {
		log.Errorf("error writing resolver resp, %s", err)
	}
}
示例#9
0
文件: resolver.go 项目: vmware/vic
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
	var (
		extConn net.Conn
		resp    *dns.Msg
		err     error
		writer  dns.ResponseWriter
	)

	if query == nil || len(query.Question) == 0 {
		return
	}
	name := query.Question[0].Name
	if query.Question[0].Qtype == dns.TypeA {
		resp, err = r.handleIPQuery(name, query, netutils.IPv4)
	} else if query.Question[0].Qtype == dns.TypeAAAA {
		resp, err = r.handleIPQuery(name, query, netutils.IPv6)
	} else if query.Question[0].Qtype == dns.TypePTR {
		resp, err = r.handlePTRQuery(name, query)
	}

	if err != nil {
		log.Error(err)
		return
	}

	proto := w.LocalAddr().Network()
	maxSize := 0
	if proto == "tcp" {
		maxSize = dns.MaxMsgSize - 1
	} else if proto == "udp" {
		optRR := query.IsEdns0()
		if optRR != nil {
			maxSize = int(optRR.UDPSize())
		}
		if maxSize < defaultRespSize {
			maxSize = defaultRespSize
		}
	}

	if resp != nil {
		if resp.Len() > maxSize {
			truncateResp(resp, maxSize, proto == "tcp")
		}
		writer = w
	} else {
		queryID := query.Id
		for i := 0; i < maxExtDNS; i++ {
			extDNS := &r.extDNSList[i]
			if extDNS.ipStr == "" {
				break
			}
			extConnect := func() {
				addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
			}

			// For udp clients connection is persisted to reuse for further queries.
			// Accessing extDNS.extConn be a race here between go rouines. Hence the
			// connection setup is done in a Once block and fetch the extConn again
			extConn = extDNS.extConn
			if extConn == nil || proto == "tcp" {
				if proto == "udp" {
					extDNS.extOnce.Do(func() {
						r.sb.execFunc(extConnect)
						extDNS.extConn = extConn
					})
					extConn = extDNS.extConn
				} else {
					r.sb.execFunc(extConnect)
				}
				if err != nil {
					log.Debugf("Connect failed, %s", err)
					continue
				}
			}
			// If two go routines are executing in parralel one will
			// block on the Once.Do and in case of error connecting
			// to the external server it will end up with a nil err
			// but extConn also being nil.
			if extConn == nil {
				continue
			}
			log.Debugf("Query %s[%d] from %s, forwarding to %s:%s", name, query.Question[0].Qtype,
				extConn.LocalAddr().String(), proto, extDNS.ipStr)

			// Timeout has to be set for every IO operation.
			extConn.SetDeadline(time.Now().Add(extIOTimeout))
			co := &dns.Conn{Conn: extConn}

			// forwardQueryStart stores required context to mux multiple client queries over
			// one connection; and limits the number of outstanding concurrent queries.
			if r.forwardQueryStart(w, query, queryID) == false {
				old := r.tStamp
				r.tStamp = time.Now()
				if r.tStamp.Sub(old) > logInterval {
					log.Errorf("More than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
				}
				continue
			}

			defer func() {
				if proto == "tcp" {
					co.Close()
				}
			}()
			err = co.WriteMsg(query)
			if err != nil {
				r.forwardQueryEnd(w, query)
				log.Debugf("Send to DNS server failed, %s", err)
				continue
			}

			resp, err = co.ReadMsg()
			if err != nil {
				r.forwardQueryEnd(w, query)
				log.Debugf("Read from DNS server failed, %s", err)
				continue
			}

			// Retrieves the context for the forwarded query and returns the client connection
			// to send the reply to
			writer = r.forwardQueryEnd(w, resp)
			if writer == nil {
				continue
			}

			resp.Compress = true
			break
		}
		if resp == nil || writer == nil {
			return
		}
	}

	if writer == nil {
		return
	}
	if err = writer.WriteMsg(resp); err != nil {
		log.Errorf("error writing resolver resp, %s", err)
	}
}
示例#10
0
文件: dns.go 项目: kjplatz/vic
// HandleVIC returns a response to a container name/id request
func (s *Server) HandleVIC(w mdns.ResponseWriter, r *mdns.Msg) (bool, error) {
	defer trace.End(trace.Begin(r.String()))

	question := r.Question[0]

	ctx := network.DefaultContext
	if ctx == nil {
		log.Errorf("DefaultContext is not initialized")
		return false, fmt.Errorf("DefaultContext is not initialized")
	}

	clientIP, _, err := net.SplitHostPort(w.RemoteAddr().String())
	if err != nil {
		log.Errorf("SplitHostPort failed: %q", err)
		return false, err
	}

	log.Debugf("RemoteAddr: %s", clientIP)
	ip := net.ParseIP(clientIP)

	var name string
	var domain string
	var scopename string

	name = strings.TrimSuffix(question.Name, ".")
	// Do we have a domain?
	i := strings.IndexRune(name, '.')
	if i >= 0 {
		name, domain = name[:i], name[i+1:]
	}

	// first look for the network alias
	c := ctx.Container(uid.UID(name))
	if c == nil {
		// find the scope of the request
		scopes, _ := ctx.Scopes(nil)
		// FIXME: We are doing linear search here
		for i := range scopes {
			s := scopes[i].Subnet()
			if s.Contains(ip) {
				scopename = scopes[i].Name()
				log.Debugf("Found the scope, using %s", scopename)

				// convert for or foo.bar to bar:foo
				if domain == "" || domain == scopename {
					name = scopename + ":" + name
					break
				}
			}
		}

		c = ctx.Container(uid.UID(name))
		if c == nil {
			log.Debugf("Can't find the container: %q", name)
			return false, fmt.Errorf("Can't find the container: %q", name)
		}
	}

	var answer []mdns.RR
	for _, e := range c.Endpoints() {
		log.Debugf("Working on %s", e.Scope().Name())

		if scopename != "" && e.Scope().Name() != scopename {
			log.Debugf("Skipping non-matching scope %s", e.Scope().Name())
			continue
		}

		if e.IP().IsUnspecified() {
			log.Debugf("Skipping unspecified IP for %s", e.Scope().Name())
			continue
		}

		// FIXME: Add AAAA when we support it
		rr := &mdns.A{
			Hdr: mdns.RR_Header{
				Name:   question.Name,
				Rrtype: mdns.TypeA,
				Class:  mdns.ClassINET,
				Ttl:    uint32(DefaultTTL.Seconds()),
			},
			A: e.IP(),
		}
		answer = append(answer, rr)
	}

	// Start crafting reply msg
	m := &mdns.Msg{
		MsgHdr: mdns.MsgHdr{
			Authoritative:      true,
			RecursionAvailable: true,
		},
		Compress: true,
	}
	m.SetReply(r)

	m.Answer = append(m.Answer, answer...)

	// Which protocol we are talking
	tcp := false
	if _, ok := w.LocalAddr().(*net.TCPAddr); ok {
		tcp = true
	}

	// 512 byte payload guarantees that DNS packets can be reassembled if fragmented in transit.
	bufsize := 512

	// With EDNS0 in use a larger payload size can be specified.
	if o := r.IsEdns0(); o != nil {
		bufsize = int(o.UDPSize())
	}

	// Make sure we are not smaller than 512
	if bufsize < 512 {
		bufsize = 512
	}

	// With TCP we can send up to 64K
	if tcp {
		bufsize = mdns.MaxMsgSize - 1
	}

	// Trim the answer RRs one by one till the whole message fits within the reply size
	if m.Len() > bufsize {
		if tcp {
			m.Truncated = true
		}

		for m.Len() > bufsize {
			m.Answer = m.Answer[:len(m.Answer)-1]
		}
	}

	if err := w.WriteMsg(m); err != nil {
		log.Errorf("Error writing response, %s", err)
		return true, err
	}
	w.Close()

	return true, nil
}
示例#11
0
文件: dns.go 项目: vmware/vic
// HandleVIC returns a response to a container name/id request
func (s *Server) HandleVIC(w mdns.ResponseWriter, r *mdns.Msg) (bool, error) {
	defer trace.End(trace.Begin(r.String()))

	question := r.Question[0]

	ctx := network.DefaultContext
	if ctx == nil {
		log.Errorf("DefaultContext is not initialized")
		return false, fmt.Errorf("DefaultContext is not initialized")
	}

	clientIP, _, err := net.SplitHostPort(w.RemoteAddr().String())
	if err != nil {
		log.Errorf("SplitHostPort failed: %q", err)
		return false, err
	}

	log.Debugf("RemoteAddr: %s", clientIP)
	ip := net.ParseIP(clientIP)

	var name string
	var domain string

	name = strings.TrimSuffix(question.Name, ".")
	// Do we have a domain?
	i := strings.IndexRune(name, '.')
	if i >= 0 {
		name, domain = name[:i], name[i+1:]
	}

	// get the requesting container's endpoint
	e := ctx.ContainerByAddr(ip)
	if e == nil {
		return false, fmt.Errorf("Could not find requesting container with ip %s", ip)
	}
	scope := e.Scope()

	if domain != "" && scope.Name() != domain {
		return false, fmt.Errorf("Inter-scope request for container %s in %s from %s", name, domain, scope.Name())
	}

	// container specific alias search
	c := ctx.Container(fmt.Sprintf("%s:%s:%s", scope.Name(), e.Container().Name(), name))
	if c == nil {
		// scope-wide search
		c = ctx.Container(fmt.Sprintf("%s:%s", scope.Name(), name))
	}

	if c == nil {
		log.Debugf("Can't find the container: %q", name)
		return false, fmt.Errorf("Can't find the container: %q", name)
	}

	e = c.Endpoint(scope)
	if e.IP().IsUnspecified() {
		return false, fmt.Errorf("No ip for container %q", name)
	}

	// FIXME: Add AAAA when we support it
	answer := []mdns.RR{
		&mdns.A{
			Hdr: mdns.RR_Header{
				Name:   question.Name,
				Rrtype: mdns.TypeA,
				Class:  mdns.ClassINET,
				Ttl:    uint32(DefaultTTL.Seconds()),
			},
			A: e.IP(),
		},
	}

	// Start crafting reply msg
	m := &mdns.Msg{
		MsgHdr: mdns.MsgHdr{
			Authoritative:      true,
			RecursionAvailable: true,
		},
		Compress: true,
	}
	m.SetReply(r)

	m.Answer = append(m.Answer, answer...)

	// Which protocol we are talking
	tcp := false
	if _, ok := w.LocalAddr().(*net.TCPAddr); ok {
		tcp = true
	}

	// 512 byte payload guarantees that DNS packets can be reassembled if fragmented in transit.
	bufsize := 512

	// With EDNS0 in use a larger payload size can be specified.
	if o := r.IsEdns0(); o != nil {
		bufsize = int(o.UDPSize())
	}

	// Make sure we are not smaller than 512
	if bufsize < 512 {
		bufsize = 512
	}

	// With TCP we can send up to 64K
	if tcp {
		bufsize = mdns.MaxMsgSize - 1
	}

	// Trim the answer RRs one by one till the whole message fits within the reply size
	if m.Len() > bufsize {
		if tcp {
			m.Truncated = true
		}

		for m.Len() > bufsize {
			m.Answer = m.Answer[:len(m.Answer)-1]
		}
	}

	if err := w.WriteMsg(m); err != nil {
		log.Errorf("Error writing response, %s", err)
		return true, err
	}
	w.Close()

	return true, nil
}