// interceptRequest gets called upon each DNS request, and we determine // whether we want to deal with it or not func interceptRequest(w dns.ResponseWriter, r *dns.Msg) { m := r.Copy() err := error(nil) defer w.Close() if len(r.Question) < 1 { return } // Hijack the response to point to us instead if matchesCriteria(r.Question[0].Name) { fmt.Println("Matches!", r.Question[0].Name) if !(inAddressCache(r.Question[0].Name)) { updateAddressCache(r) } m = hijackResponse(r) // Pass it upstream, return the answer } else { //fmt.Println("Passing on ", r.Question[0].Name) m, err = upstreamLookup(r) if err != nil { fmt.Println("Error when passing request through upstream - network problem?") // in this instance, our response (m) has no answer } } w.WriteMsg(m) }
// handleDNS is a handler function to actualy perform the dns querey response func (c *CatchAll) handleDNS(w dns.ResponseWriter, r *dns.Msg) { defer w.Close() var rr dns.RR domainSpoof := r.Question[0].Name msgResp := new(dns.Msg) msgResp.SetReply(r) msgResp.Compress = false rr = new(dns.A) if c.SpoofDomain { rr.(*dns.A).Hdr = dns.RR_Header{Name: domainSpoof, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} } else { rr.(*dns.A).Hdr = dns.RR_Header{Name: c.Domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} } rr.(*dns.A).A = c.IP switch r.Question[0].Qtype { case dns.TypeA: msgResp.Answer = append(msgResp.Answer, rr) default: log.Warnf("Unknown dns type %T", r.Question[0].Qtype) return } w.WriteMsg(msgResp) }
func (s *Server) handle(w dns.ResponseWriter, request *dns.Msg) { // Always close the writer defer w.Close() // Capture starting time for measuring message response time var start time.Time start = time.Now() // Setup the default response var response *dns.Msg response = &dns.Msg{} response.SetReply(request) response.Compress = true // Lookup answers to any of the questions for _, question := range request.Question { // Capture starting time for measuring lookup var lookupStart time.Time lookupStart = time.Now() // Perform lookup for this question var records []dns.RR records = s.registry.Lookup(question.Name, question.Qtype, question.Qclass) // Capture ending and elapsed time var lookupElapsed time.Duration lookupElapsed = time.Since(lookupStart) // Append results to the response response.Answer = append(response.Answer, records...) // If StatsD is enabled, record some metrics if s.statsd != nil { var tags []string tags = []string{ fmt.Sprintf("name:%s", question.Name), fmt.Sprintf("qtype:%s", dns.TypeToString[question.Qtype]), fmt.Sprintf("qclass:%s", dns.ClassToString[question.Qclass]), } s.statsd.TimeInMilliseconds("lookup.time", lookupElapsed.Seconds()*1000.0, tags, 1) s.statsd.Histogram("lookup.answer", float64(len(records)), tags, 1) s.statsd.Count("request.question", 1, tags, 1) } } // Respond to the request w.WriteMsg(response) // Record any ending metrics if s.statsd != nil { var elapsed time.Duration elapsed = time.Since(start) s.statsd.TimeInMilliseconds("request.time", elapsed.Seconds()*1000.0, nil, 1) } }
func dnsHandler(w dns.ResponseWriter, r *dns.Msg) { defer w.Close() m := new(dns.Msg) m.SetReply(r) m.Compress = false for _, q := range r.Question { fmt.Printf("dns-srv: Query -- [%s] %s\n", q.Name, dns.TypeToString[q.Qtype]) switch q.Qtype { case dns.TypeA: record := new(dns.A) record.Hdr = dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0, } record.A = net.ParseIP("127.0.0.1") m.Answer = append(m.Answer, record) case dns.TypeMX: record := new(dns.MX) record.Hdr = dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0, } record.Mx = "mail." + q.Name record.Preference = 10 m.Answer = append(m.Answer, record) } } w.WriteMsg(m) return }
func route(w dns.ResponseWriter, req *dns.Msg) { keyP, err := getKey(req) if err != nil { failWithRcode(w, req, dns.RcodeRefused) return } if handleSpecialNames(w, req) { return } maxPayloadSize := getMaxPayloadSize(req) var resp *dns.Msg cacheValP, _ := cache.Get(*keyP) if cacheValP != nil { cacheVal := cacheValP.(CacheVal) remaining := -time.Since(cacheVal.ValidUntil) if remaining > 0 { resp = cacheVal.Response.Copy() resp.Id = req.Id resp.Question = req.Question } } if *debug { question := req.Question[0] cachedStr := "" if resp != nil { cachedStr = " (cached)" } log.Printf("%v\t%v %v%v\n", w.RemoteAddr(), question.Name, dns.TypeToString[question.Qtype], cachedStr) } if resp == nil { slipValue := atomic.LoadUint32(&slip) if slipValue > 0 && slipValue%2 == 0 { atomic.CompareAndSwapUint32(&slip, slipValue, slipValue+1) if slipValue%4 == 0 { sendTruncated(w, req.MsgHdr) } else { w.Close() } return } } if resp == nil { resp, err = resolve(req, keyP.DNSSEC) if err == nil { validUntil := time.Now().Add(getMinTTL(resp)) cache.Add(*keyP, CacheVal{ValidUntil: validUntil, Response: resp}) } else { if cacheValP == nil { w.Close() return } cacheVal := cacheValP.(CacheVal) resp = cacheVal.Response.Copy() resp.Id = req.Id resp.Question = req.Question } } packed, _ := resp.Pack() packedLen := len(packed) if uint16(packedLen) > maxPayloadSize { sendTruncated(w, resp.MsgHdr) } else { w.WriteMsg(resp) } }
// 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 }