Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.0 KiB
111 lines
3.0 KiB
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) |
|
}
|
|
|