msg_truncate.go 3.4 KB

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