msg_truncate.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package dns
  2. // Truncate ensures the reply message will fit into the requested buffer
  3. // size by removing records that exceed the requested size.
  4. //
  5. // It will first check if the reply fits without compression and then with
  6. // compression. If it won't fit with compression, Truncate then walks the
  7. // record adding as many records as possible without exceeding the
  8. // requested buffer size.
  9. //
  10. // The TC bit will be set if any records were excluded from the message.
  11. // This indicates to that the client should retry over TCP.
  12. //
  13. // According to RFC 2181, the TC bit should only be set if not all of the
  14. // "required" RRs can be included in the response. Unfortunately, we have
  15. // no way of knowing which RRs are required so we set the TC bit if any RR
  16. // had to be omitted from the response.
  17. //
  18. // The appropriate buffer size can be retrieved from the requests OPT
  19. // record, if present, and is transport specific otherwise. dns.MinMsgSize
  20. // should be used for UDP requests without an OPT record, and
  21. // dns.MaxMsgSize for TCP requests without an OPT record.
  22. func (dns *Msg) Truncate(size int) {
  23. if dns.IsTsig() != nil {
  24. // To simplify this implementation, we don't perform
  25. // truncation on responses with a TSIG record.
  26. return
  27. }
  28. // RFC 6891 mandates that the payload size in an OPT record
  29. // less than 512 bytes must be treated as equal to 512 bytes.
  30. //
  31. // For ease of use, we impose that restriction here.
  32. if size < 512 {
  33. size = 512
  34. }
  35. l := msgLenWithCompressionMap(dns, nil) // uncompressed length
  36. if l <= size {
  37. // Don't waste effort compressing this message.
  38. dns.Compress = false
  39. return
  40. }
  41. dns.Compress = true
  42. edns0 := dns.popEdns0()
  43. if edns0 != nil {
  44. // Account for the OPT record that gets added at the end,
  45. // by subtracting that length from our budget.
  46. //
  47. // The EDNS(0) OPT record must have the root domain and
  48. // it's length is thus unaffected by compression.
  49. size -= Len(edns0)
  50. }
  51. compression := make(map[string]struct{})
  52. l = headerSize
  53. for _, r := range dns.Question {
  54. l += r.len(l, compression)
  55. }
  56. var numAnswer int
  57. if l < size {
  58. l, numAnswer = truncateLoop(dns.Answer, size, l, compression)
  59. }
  60. var numNS int
  61. if l < size {
  62. l, numNS = truncateLoop(dns.Ns, size, l, compression)
  63. }
  64. var numExtra int
  65. if l < size {
  66. l, numExtra = truncateLoop(dns.Extra, size, l, compression)
  67. }
  68. // See the function documentation for when we set this.
  69. dns.Truncated = len(dns.Answer) > numAnswer ||
  70. len(dns.Ns) > numNS || len(dns.Extra) > numExtra
  71. dns.Answer = dns.Answer[:numAnswer]
  72. dns.Ns = dns.Ns[:numNS]
  73. dns.Extra = dns.Extra[:numExtra]
  74. if edns0 != nil {
  75. // Add the OPT record back onto the additional section.
  76. dns.Extra = append(dns.Extra, edns0)
  77. }
  78. }
  79. func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) {
  80. for i, r := range rrs {
  81. if r == nil {
  82. continue
  83. }
  84. l += r.len(l, compression)
  85. if l > size {
  86. // Return size, rather than l prior to this record,
  87. // to prevent any further records being added.
  88. return size, i
  89. }
  90. if l == size {
  91. return l, i + 1
  92. }
  93. }
  94. return l, len(rrs)
  95. }