Limit the number of answers we provide to users.
This commit is contained in:
1 parent
0cb6a6269d
commit
cfcc08a914
6 files changed
+70
-11
No files matched your search
+9
-1
@@ -89,7 +89,6 @@ func getMaxReplyLen(r *dns.Msg, proto dnsProtocol) int {
|
||||
return maxLen
|
||||
}
|
||||
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
}
|
||||
@@ -105,3 +104,12 @@ func shuffleAnswers(answers []dns.RR) []dns.RR {
|
||||
|
||||
return answers
|
||||
}
|
||||
|
||||
// only take the first `num` answers
|
||||
func pruneAnswers(answers []dns.RR, num int) []dns.RR {
|
||||
if num > 0 && len(answers) > num {
|
||||
// TODO: we should have some prefer locally-introduced answers, etc...
|
||||
return answers[:num]
|
||||
}
|
||||
return answers
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package nameserver
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
wt "github.com/weaveworks/weave/testing"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Check that we can prune an answer
|
||||
func TestPrune(t *testing.T) {
|
||||
InitDefaultLogging(testing.Verbose())
|
||||
Info.Println("TestPrune starting")
|
||||
|
||||
questionMsg := new(dns.Msg)
|
||||
questionMsg.SetQuestion("name", dns.TypeA)
|
||||
questionMsg.RecursionDesired = true
|
||||
question := &questionMsg.Question[0]
|
||||
records := []ZoneRecord{
|
||||
Record{"name", net.ParseIP("10.0.1.1"), 0, 0, 0},
|
||||
Record{"name", net.ParseIP("10.0.1.2"), 0, 0, 0},
|
||||
Record{"name", net.ParseIP("10.0.1.3"), 0, 0, 0},
|
||||
Record{"name", net.ParseIP("10.0.1.4"), 0, 0, 0},
|
||||
}
|
||||
|
||||
reply := makeAddressReply(questionMsg, question, records)
|
||||
reply.Answer[0].Header().Ttl = localTTL
|
||||
|
||||
pruned := pruneAnswers(reply.Answer, 1)
|
||||
wt.AssertEqualInt(t, len(pruned), 1, "wrong number of answers")
|
||||
|
||||
pruned = pruneAnswers(reply.Answer, 2)
|
||||
wt.AssertEqualInt(t, len(pruned), 2, "wrong number of answers")
|
||||
|
||||
pruned = pruneAnswers(reply.Answer, 0)
|
||||
wt.AssertEqualInt(t, len(pruned), len(records), "wrong number of answers")
|
||||
}
|
||||
+13
-6
@@ -18,6 +18,7 @@ const (
|
||||
DefaultUDPBuflen = 4096 // bigger than the default 512
|
||||
DefaultCacheLen = 8192 // default cache capacity
|
||||
DefaultTimeout = 5 // default timeout for DNS resolutions
|
||||
DefaultMaxAnswers = 1 // default number of answers provided to users
|
||||
)
|
||||
|
||||
type DNSServerConfig struct {
|
||||
@@ -45,6 +46,8 @@ type DNSServerConfig struct {
|
||||
Clock clock.Clock
|
||||
// (Optional) Listening socket read timeout (in milliseconds)
|
||||
ListenReadTimeout int
|
||||
// Maximum number of answers provided to users
|
||||
MaxAnswers int
|
||||
}
|
||||
|
||||
type dnsProtocol uint8
|
||||
@@ -88,6 +91,7 @@ type DNSServer struct {
|
||||
lst net.Listener
|
||||
cache ZoneCache
|
||||
cacheDisabled bool
|
||||
maxAnswers int
|
||||
negLocalTTL int
|
||||
timeout time.Duration
|
||||
readTimeout time.Duration
|
||||
@@ -107,6 +111,7 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
|
||||
timeout: time.Duration(config.Timeout) * time.Millisecond,
|
||||
readTimeout: time.Duration(config.ListenReadTimeout) * time.Millisecond,
|
||||
cacheDisabled: false,
|
||||
maxAnswers: DefaultMaxAnswers,
|
||||
negLocalTTL: negLocalTTL,
|
||||
clock: config.Clock,
|
||||
}
|
||||
@@ -140,6 +145,9 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
|
||||
if config.UDPBufLen > 0 {
|
||||
s.udpBuf = config.UDPBufLen
|
||||
}
|
||||
if config.MaxAnswers > 0 {
|
||||
s.maxAnswers = config.MaxAnswers
|
||||
}
|
||||
if config.CacheNegLocalTTL > 0 {
|
||||
s.negLocalTTL = config.CacheNegLocalTTL
|
||||
}
|
||||
@@ -301,17 +309,16 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
|
||||
if err == errNoLocalReplies {
|
||||
Debug.Printf("[dns msgid %d] Cached no-local-replies", r.MsgHdr.Id)
|
||||
fallback(w, r)
|
||||
return
|
||||
} else {
|
||||
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
|
||||
w.WriteMsg(makeDNSFailResponse(r))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
if reply != nil {
|
||||
reply.Answer = pruneAnswers(shuffleAnswers(reply.Answer), s.maxAnswers)
|
||||
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
|
||||
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
|
||||
reply.Answer = shuffleAnswers(reply.Answer)
|
||||
w.WriteMsg(reply)
|
||||
return
|
||||
}
|
||||
@@ -334,10 +341,10 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
|
||||
} else {
|
||||
m := msgBuilder(r, &q, answers)
|
||||
m.Authoritative = true
|
||||
m.Answer = shuffleAnswers(m.Answer)
|
||||
Debug.Printf("[dns msgid %d] Sending response: %s/%s:%s [code:%s]",
|
||||
m.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name, answers, dns.RcodeToString[m.Rcode])
|
||||
maybeCache(m, nullTTL, 0)
|
||||
m.Answer = pruneAnswers(shuffleAnswers(m.Answer), s.maxAnswers)
|
||||
Debug.Printf("[dns msgid %d] Sending response: %s/%d answers [code:%s]",
|
||||
m.MsgHdr.Id, dns.TypeToString[q.Qtype], len(m.Answer), dns.RcodeToString[m.Rcode])
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ func TestServerCacheRefresh(t *testing.T) {
|
||||
Cache: cache,
|
||||
Clock: clk,
|
||||
ListenReadTimeout: testSocketTimeout,
|
||||
MaxAnswers: 4,
|
||||
})
|
||||
wt.AssertNoErr(t, err)
|
||||
go srv.Start()
|
||||
|
||||
+6
-4
@@ -44,10 +44,9 @@ $ docker attach $shell1
|
||||
```
|
||||
|
||||
Note that it is permissible to register multiple containers with the
|
||||
same name. weaveDNS picks one address to return when asked for the
|
||||
name. Since weaveDNS removes any container that dies, this is a simple
|
||||
way to implement redundancy. In the current implementation it does
|
||||
not attempt to do load-balancing.
|
||||
same name: weaveDNS picks one address at random on each request. Since
|
||||
weaveDNS removes any container that dies, this is a simple way to
|
||||
implement redundancy.
|
||||
|
||||
Each weaveDNS container started with `launch-dns` needs to be given
|
||||
its own, unique, IP address, in a subnet that is a) common to all
|
||||
@@ -103,6 +102,9 @@ The inverse operation can be carried out using the `dns-remove` command:
|
||||
$ weave dns-remove 10.2.1.27 $shell2
|
||||
```
|
||||
|
||||
When queried about a name with multiple IPs, weaveDNS returns a random
|
||||
result from the set of IPs available.
|
||||
|
||||
## <a name="hot-swapping"></a>Hot-swapping service containers
|
||||
|
||||
If you would like to deploy a new version of a service, keep the old
|
||||
|
||||
@@ -27,6 +27,7 @@ func main() {
|
||||
refreshInterval int
|
||||
refreshWorkers int
|
||||
relevantTime int
|
||||
maxAnswers int
|
||||
cacheLen int
|
||||
cacheDisabled bool
|
||||
watch bool
|
||||
@@ -47,6 +48,7 @@ func main() {
|
||||
// advanced options
|
||||
flag.IntVar(&refreshInterval, "refresh", weavedns.DefaultRefreshInterval, "refresh interval (in secs) for local names (0=disable)")
|
||||
flag.IntVar(&refreshWorkers, "refresh-workers", weavedns.DefaultNumUpdaters, "default number of background updaters")
|
||||
flag.IntVar(&maxAnswers, "max-answers", weavedns.DefaultMaxAnswers, "maximum number of answers returned to clients (0=unlimited)")
|
||||
flag.IntVar(&relevantTime, "relevant", weavedns.DefaultRelevantTime, "life time for info in the absence of queries (in secs)")
|
||||
flag.IntVar(&udpbuf, "udpbuf", weavedns.DefaultUDPBuflen, "UDP buffer length")
|
||||
flag.IntVar(&timeout, "timeout", weavedns.DefaultTimeout, "timeout for resolutions (in millisecs)")
|
||||
@@ -102,6 +104,7 @@ func main() {
|
||||
Zone: zone,
|
||||
Port: dnsPort,
|
||||
CacheLen: cacheLen,
|
||||
MaxAnswers: maxAnswers,
|
||||
Timeout: timeout,
|
||||
UDPBufLen: udpbuf,
|
||||
CacheDisabled: cacheDisabled,
|
||||
|
||||
Reference in new issue
Block a user