edns.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Package edns provides function useful for adding/inspecting OPT records to/in messages.
  2. package edns
  3. import (
  4. "errors"
  5. "sync"
  6. "github.com/miekg/dns"
  7. )
  8. var sup = &supported{m: make(map[uint16]struct{})}
  9. type supported struct {
  10. m map[uint16]struct{}
  11. sync.RWMutex
  12. }
  13. // SetSupportedOption adds a new supported option the set of EDNS0 options that we support. Plugins typically call
  14. // this in their setup code to signal support for a new option.
  15. // By default we support:
  16. // dns.EDNS0NSID, dns.EDNS0EXPIRE, dns.EDNS0COOKIE, dns.EDNS0TCPKEEPALIVE, dns.EDNS0PADDING. These
  17. // values are not in this map and checked directly in the server.
  18. func SetSupportedOption(option uint16) {
  19. sup.Lock()
  20. sup.m[option] = struct{}{}
  21. sup.Unlock()
  22. }
  23. // SupportedOption returns true if the option code is supported as an extra EDNS0 option.
  24. func SupportedOption(option uint16) bool {
  25. sup.RLock()
  26. _, ok := sup.m[option]
  27. sup.RUnlock()
  28. return ok
  29. }
  30. // Version checks the EDNS version in the request. If error
  31. // is nil everything is OK and we can invoke the plugin. If non-nil, the
  32. // returned Msg is valid to be returned to the client (and should). For some
  33. // reason this response should not contain a question RR in the question section.
  34. func Version(req *dns.Msg) (*dns.Msg, error) {
  35. opt := req.IsEdns0()
  36. if opt == nil {
  37. return nil, nil
  38. }
  39. if opt.Version() == 0 {
  40. return nil, nil
  41. }
  42. m := new(dns.Msg)
  43. m.SetReply(req)
  44. // zero out question section, wtf.
  45. m.Question = nil
  46. o := new(dns.OPT)
  47. o.Hdr.Name = "."
  48. o.Hdr.Rrtype = dns.TypeOPT
  49. o.SetVersion(0)
  50. m.Rcode = dns.RcodeBadVers
  51. o.SetExtendedRcode(dns.RcodeBadVers)
  52. m.Extra = []dns.RR{o}
  53. return m, errors.New("EDNS0 BADVERS")
  54. }
  55. // Size returns a normalized size based on proto.
  56. func Size(proto string, size uint16) uint16 {
  57. if proto == "tcp" {
  58. return dns.MaxMsgSize
  59. }
  60. if size < dns.MinMsgSize {
  61. return dns.MinMsgSize
  62. }
  63. return size
  64. }
  65. /*
  66. The below wasn't from the edns package
  67. */
  68. // SetSizeAndDo adds an OPT record that the reflects the intent from request.
  69. func SetSizeAndDo(req, m *dns.Msg) *dns.OPT {
  70. o := req.IsEdns0()
  71. if o == nil {
  72. return nil
  73. }
  74. if mo := m.IsEdns0(); mo != nil {
  75. mo.Hdr.Name = "."
  76. mo.Hdr.Rrtype = dns.TypeOPT
  77. mo.SetVersion(0)
  78. mo.SetUDPSize(o.UDPSize())
  79. mo.Hdr.Ttl &= 0xff00 // clear flags
  80. // Assume if the message m has options set, they are OK and represent what an upstream can do.
  81. if o.Do() {
  82. mo.SetDo()
  83. }
  84. return mo
  85. }
  86. // Reuse the request's OPT record and tack it to m.
  87. o.Hdr.Name = "."
  88. o.Hdr.Rrtype = dns.TypeOPT
  89. o.SetVersion(0)
  90. o.Hdr.Ttl &= 0xff00 // clear flags
  91. if len(o.Option) > 0 {
  92. o.Option = SupportedOptions(o.Option)
  93. }
  94. m.Extra = append(m.Extra, o)
  95. return o
  96. }
  97. func SupportedOptions(o []dns.EDNS0) []dns.EDNS0 {
  98. var supported = make([]dns.EDNS0, 0, 3)
  99. // For as long as possible try avoid looking up in the map, because that need an Rlock.
  100. for _, opt := range o {
  101. switch code := opt.Option(); code {
  102. case dns.EDNS0NSID:
  103. fallthrough
  104. case dns.EDNS0COOKIE:
  105. fallthrough
  106. case dns.EDNS0SUBNET:
  107. supported = append(supported, opt)
  108. default:
  109. if SupportedOption(code) {
  110. supported = append(supported, opt)
  111. }
  112. }
  113. }
  114. return supported
  115. }