aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/miekg/dns/msg_truncate.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/miekg/dns/msg_truncate.go')
-rw-r--r--vendor/github.com/miekg/dns/msg_truncate.go111
1 files changed, 111 insertions, 0 deletions
diff --git a/vendor/github.com/miekg/dns/msg_truncate.go b/vendor/github.com/miekg/dns/msg_truncate.go
new file mode 100644
index 0000000000..a76150a861
--- /dev/null
+++ b/vendor/github.com/miekg/dns/msg_truncate.go
@@ -0,0 +1,111 @@
+package dns
+
+// Truncate ensures the reply message will fit into the requested buffer
+// size by removing records that exceed the requested size.
+//
+// It will first check if the reply fits without compression and then with
+// compression. If it won't fit with compression, Truncate then walks the
+// record adding as many records as possible without exceeding the
+// requested buffer size.
+//
+// The TC bit will be set if any records were excluded from the message.
+// This indicates to that the client should retry over TCP.
+//
+// According to RFC 2181, the TC bit should only be set if not all of the
+// "required" RRs can be included in the response. Unfortunately, we have
+// no way of knowing which RRs are required so we set the TC bit if any RR
+// had to be omitted from the response.
+//
+// The appropriate buffer size can be retrieved from the requests OPT
+// record, if present, and is transport specific otherwise. dns.MinMsgSize
+// should be used for UDP requests without an OPT record, and
+// dns.MaxMsgSize for TCP requests without an OPT record.
+func (dns *Msg) Truncate(size int) {
+ if dns.IsTsig() != nil {
+ // To simplify this implementation, we don't perform
+ // truncation on responses with a TSIG record.
+ return
+ }
+
+ // RFC 6891 mandates that the payload size in an OPT record
+ // less than 512 bytes must be treated as equal to 512 bytes.
+ //
+ // For ease of use, we impose that restriction here.
+ if size < 512 {
+ size = 512
+ }
+
+ l := msgLenWithCompressionMap(dns, nil) // uncompressed length
+ if l <= size {
+ // Don't waste effort compressing this message.
+ dns.Compress = false
+ return
+ }
+
+ dns.Compress = true
+
+ edns0 := dns.popEdns0()
+ if edns0 != nil {
+ // Account for the OPT record that gets added at the end,
+ // by subtracting that length from our budget.
+ //
+ // The EDNS(0) OPT record must have the root domain and
+ // it's length is thus unaffected by compression.
+ size -= Len(edns0)
+ }
+
+ compression := make(map[string]struct{})
+
+ l = headerSize
+ for _, r := range dns.Question {
+ l += r.len(l, compression)
+ }
+
+ var numAnswer int
+ if l < size {
+ l, numAnswer = truncateLoop(dns.Answer, size, l, compression)
+ }
+
+ var numNS int
+ if l < size {
+ l, numNS = truncateLoop(dns.Ns, size, l, compression)
+ }
+
+ var numExtra int
+ if l < size {
+ _, numExtra = truncateLoop(dns.Extra, size, l, compression)
+ }
+
+ // See the function documentation for when we set this.
+ dns.Truncated = len(dns.Answer) > numAnswer ||
+ len(dns.Ns) > numNS || len(dns.Extra) > numExtra
+
+ dns.Answer = dns.Answer[:numAnswer]
+ dns.Ns = dns.Ns[:numNS]
+ dns.Extra = dns.Extra[:numExtra]
+
+ if edns0 != nil {
+ // Add the OPT record back onto the additional section.
+ dns.Extra = append(dns.Extra, edns0)
+ }
+}
+
+func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {
+ for i, r := range rrs {
+ if r == nil {
+ continue
+ }
+
+ l += r.len(l, compression)
+ if l > size {
+ // Return size, rather than l prior to this record,
+ // to prevent any further records being added.
+ return size, i
+ }
+ if l == size {
+ return l, i + 1
+ }
+ }
+
+ return l, len(rrs)
+}