123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- // Package edns provides function useful for adding/inspecting OPT records to/in messages.
- package edns
- import (
- "errors"
- "sync"
- "github.com/miekg/dns"
- )
- var sup = &supported{m: make(map[uint16]struct{})}
- type supported struct {
- m map[uint16]struct{}
- sync.RWMutex
- }
- // SetSupportedOption adds a new supported option the set of EDNS0 options that we support. Plugins typically call
- // this in their setup code to signal support for a new option.
- // By default we support:
- // dns.EDNS0NSID, dns.EDNS0EXPIRE, dns.EDNS0COOKIE, dns.EDNS0TCPKEEPALIVE, dns.EDNS0PADDING. These
- // values are not in this map and checked directly in the server.
- func SetSupportedOption(option uint16) {
- sup.Lock()
- sup.m[option] = struct{}{}
- sup.Unlock()
- }
- // SupportedOption returns true if the option code is supported as an extra EDNS0 option.
- func SupportedOption(option uint16) bool {
- sup.RLock()
- _, ok := sup.m[option]
- sup.RUnlock()
- return ok
- }
- // Version checks the EDNS version in the request. If error
- // is nil everything is OK and we can invoke the plugin. If non-nil, the
- // returned Msg is valid to be returned to the client (and should). For some
- // reason this response should not contain a question RR in the question section.
- func Version(req *dns.Msg) (*dns.Msg, error) {
- opt := req.IsEdns0()
- if opt == nil {
- return nil, nil
- }
- if opt.Version() == 0 {
- return nil, nil
- }
- m := new(dns.Msg)
- m.SetReply(req)
- // zero out question section, wtf.
- m.Question = nil
- o := new(dns.OPT)
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- o.SetVersion(0)
- m.Rcode = dns.RcodeBadVers
- o.SetExtendedRcode(dns.RcodeBadVers)
- m.Extra = []dns.RR{o}
- return m, errors.New("EDNS0 BADVERS")
- }
- // Size returns a normalized size based on proto.
- func Size(proto string, size uint16) uint16 {
- if proto == "tcp" {
- return dns.MaxMsgSize
- }
- if size < dns.MinMsgSize {
- return dns.MinMsgSize
- }
- return size
- }
- /*
- The below wasn't from the edns package
- */
- // SetSizeAndDo adds an OPT record that the reflects the intent from request.
- func SetSizeAndDo(req, m *dns.Msg) *dns.OPT {
- o := req.IsEdns0()
- if o == nil {
- return nil
- }
- if mo := m.IsEdns0(); mo != nil {
- mo.Hdr.Name = "."
- mo.Hdr.Rrtype = dns.TypeOPT
- mo.SetVersion(0)
- mo.SetUDPSize(o.UDPSize())
- mo.Hdr.Ttl &= 0xff00 // clear flags
- // Assume if the message m has options set, they are OK and represent what an upstream can do.
- if o.Do() {
- mo.SetDo()
- }
- return mo
- }
- // Reuse the request's OPT record and tack it to m.
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- o.SetVersion(0)
- o.Hdr.Ttl &= 0xff00 // clear flags
- if len(o.Option) > 0 {
- o.Option = SupportedOptions(o.Option)
- }
- m.Extra = append(m.Extra, o)
- return o
- }
- func SupportedOptions(o []dns.EDNS0) []dns.EDNS0 {
- var supported = make([]dns.EDNS0, 0, 3)
- // For as long as possible try avoid looking up in the map, because that need an Rlock.
- for _, opt := range o {
- switch code := opt.Option(); code {
- case dns.EDNS0NSID:
- fallthrough
- case dns.EDNS0COOKIE:
- fallthrough
- case dns.EDNS0SUBNET:
- supported = append(supported, opt)
- default:
- if SupportedOption(code) {
- supported = append(supported, opt)
- }
- }
- }
- return supported
- }
|