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