...

Source file src/github.com/miekg/dns/defaults.go

Documentation: github.com/miekg/dns

     1  package dns
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  const hexDigit = "0123456789abcdef"
    11  
    12  // Everything is assumed in ClassINET.
    13  
    14  // SetReply creates a reply message from a request message.
    15  func (dns *Msg) SetReply(request *Msg) *Msg {
    16  	dns.Id = request.Id
    17  	dns.Response = true
    18  	dns.Opcode = request.Opcode
    19  	if dns.Opcode == OpcodeQuery {
    20  		dns.RecursionDesired = request.RecursionDesired // Copy rd bit
    21  		dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
    22  	}
    23  	dns.Rcode = RcodeSuccess
    24  	if len(request.Question) > 0 {
    25  		dns.Question = []Question{request.Question[0]}
    26  	}
    27  	return dns
    28  }
    29  
    30  // SetQuestion creates a question message, it sets the Question
    31  // section, generates an Id and sets the RecursionDesired (RD)
    32  // bit to true.
    33  func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
    34  	dns.Id = Id()
    35  	dns.RecursionDesired = true
    36  	dns.Question = make([]Question, 1)
    37  	dns.Question[0] = Question{z, t, ClassINET}
    38  	return dns
    39  }
    40  
    41  // SetNotify creates a notify message, it sets the Question
    42  // section, generates an Id and sets the Authoritative (AA)
    43  // bit to true.
    44  func (dns *Msg) SetNotify(z string) *Msg {
    45  	dns.Opcode = OpcodeNotify
    46  	dns.Authoritative = true
    47  	dns.Id = Id()
    48  	dns.Question = make([]Question, 1)
    49  	dns.Question[0] = Question{z, TypeSOA, ClassINET}
    50  	return dns
    51  }
    52  
    53  // SetRcode creates an error message suitable for the request.
    54  func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
    55  	dns.SetReply(request)
    56  	dns.Rcode = rcode
    57  	return dns
    58  }
    59  
    60  // SetRcodeFormatError creates a message with FormError set.
    61  func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
    62  	dns.Rcode = RcodeFormatError
    63  	dns.Opcode = OpcodeQuery
    64  	dns.Response = true
    65  	dns.Authoritative = false
    66  	dns.Id = request.Id
    67  	return dns
    68  }
    69  
    70  // SetUpdate makes the message a dynamic update message. It
    71  // sets the ZONE section to: z, TypeSOA, ClassINET.
    72  func (dns *Msg) SetUpdate(z string) *Msg {
    73  	dns.Id = Id()
    74  	dns.Response = false
    75  	dns.Opcode = OpcodeUpdate
    76  	dns.Compress = false // BIND9 cannot handle compression
    77  	dns.Question = make([]Question, 1)
    78  	dns.Question[0] = Question{z, TypeSOA, ClassINET}
    79  	return dns
    80  }
    81  
    82  // SetIxfr creates message for requesting an IXFR.
    83  func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {
    84  	dns.Id = Id()
    85  	dns.Question = make([]Question, 1)
    86  	dns.Ns = make([]RR, 1)
    87  	s := new(SOA)
    88  	s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
    89  	s.Serial = serial
    90  	s.Ns = ns
    91  	s.Mbox = mbox
    92  	dns.Question[0] = Question{z, TypeIXFR, ClassINET}
    93  	dns.Ns[0] = s
    94  	return dns
    95  }
    96  
    97  // SetAxfr creates message for requesting an AXFR.
    98  func (dns *Msg) SetAxfr(z string) *Msg {
    99  	dns.Id = Id()
   100  	dns.Question = make([]Question, 1)
   101  	dns.Question[0] = Question{z, TypeAXFR, ClassINET}
   102  	return dns
   103  }
   104  
   105  // SetTsig appends a TSIG RR to the message.
   106  // This is only a skeleton TSIG RR that is added as the last RR in the
   107  // additional section. The TSIG is calculated when the message is being send.
   108  func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
   109  	t := new(TSIG)
   110  	t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
   111  	t.Algorithm = algo
   112  	t.Fudge = fudge
   113  	t.TimeSigned = uint64(timesigned)
   114  	t.OrigId = dns.Id
   115  	dns.Extra = append(dns.Extra, t)
   116  	return dns
   117  }
   118  
   119  // SetEdns0 appends a EDNS0 OPT RR to the message.
   120  // TSIG should always the last RR in a message.
   121  func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
   122  	e := new(OPT)
   123  	e.Hdr.Name = "."
   124  	e.Hdr.Rrtype = TypeOPT
   125  	e.SetUDPSize(udpsize)
   126  	if do {
   127  		e.SetDo()
   128  	}
   129  	dns.Extra = append(dns.Extra, e)
   130  	return dns
   131  }
   132  
   133  // IsTsig checks if the message has a TSIG record as the last record
   134  // in the additional section. It returns the TSIG record found or nil.
   135  func (dns *Msg) IsTsig() *TSIG {
   136  	if len(dns.Extra) > 0 {
   137  		if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
   138  			return dns.Extra[len(dns.Extra)-1].(*TSIG)
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  // IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
   145  // record in the additional section will do. It returns the OPT record
   146  // found or nil.
   147  func (dns *Msg) IsEdns0() *OPT {
   148  	// RFC 6891, Section 6.1.1 allows the OPT record to appear
   149  	// anywhere in the additional record section, but it's usually at
   150  	// the end so start there.
   151  	for i := len(dns.Extra) - 1; i >= 0; i-- {
   152  		if dns.Extra[i].Header().Rrtype == TypeOPT {
   153  			return dns.Extra[i].(*OPT)
   154  		}
   155  	}
   156  	return nil
   157  }
   158  
   159  // popEdns0 is like IsEdns0, but it removes the record from the message.
   160  func (dns *Msg) popEdns0() *OPT {
   161  	// RFC 6891, Section 6.1.1 allows the OPT record to appear
   162  	// anywhere in the additional record section, but it's usually at
   163  	// the end so start there.
   164  	for i := len(dns.Extra) - 1; i >= 0; i-- {
   165  		if dns.Extra[i].Header().Rrtype == TypeOPT {
   166  			opt := dns.Extra[i].(*OPT)
   167  			dns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...)
   168  			return opt
   169  		}
   170  	}
   171  	return nil
   172  }
   173  
   174  // IsDomainName checks if s is a valid domain name, it returns the number of
   175  // labels and true, when a domain name is valid.  Note that non fully qualified
   176  // domain name is considered valid, in this case the last label is counted in
   177  // the number of labels.  When false is returned the number of labels is not
   178  // defined.  Also note that this function is extremely liberal; almost any
   179  // string is a valid domain name as the DNS is 8 bit protocol. It checks if each
   180  // label fits in 63 characters and that the entire name will fit into the 255
   181  // octet wire format limit.
   182  func IsDomainName(s string) (labels int, ok bool) {
   183  	// XXX: The logic in this function was copied from packDomainName and
   184  	// should be kept in sync with that function.
   185  
   186  	const lenmsg = 256
   187  
   188  	if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata.
   189  		return 0, false
   190  	}
   191  
   192  	s = Fqdn(s)
   193  
   194  	// Each dot ends a segment of the name. Except for escaped dots (\.), which
   195  	// are normal dots.
   196  
   197  	var (
   198  		off    int
   199  		begin  int
   200  		wasDot bool
   201  	)
   202  	for i := 0; i < len(s); i++ {
   203  		switch s[i] {
   204  		case '\\':
   205  			if off+1 > lenmsg {
   206  				return labels, false
   207  			}
   208  
   209  			// check for \DDD
   210  			if isDDD(s[i+1:]) {
   211  				i += 3
   212  				begin += 3
   213  			} else {
   214  				i++
   215  				begin++
   216  			}
   217  
   218  			wasDot = false
   219  		case '.':
   220  			if i == 0 && len(s) > 1 {
   221  				// leading dots are not legal except for the root zone
   222  				return labels, false
   223  			}
   224  
   225  			if wasDot {
   226  				// two dots back to back is not legal
   227  				return labels, false
   228  			}
   229  			wasDot = true
   230  
   231  			labelLen := i - begin
   232  			if labelLen >= 1<<6 { // top two bits of length must be clear
   233  				return labels, false
   234  			}
   235  
   236  			// off can already (we're in a loop) be bigger than lenmsg
   237  			// this happens when a name isn't fully qualified
   238  			off += 1 + labelLen
   239  			if off > lenmsg {
   240  				return labels, false
   241  			}
   242  
   243  			labels++
   244  			begin = i + 1
   245  		default:
   246  			wasDot = false
   247  		}
   248  	}
   249  
   250  	return labels, true
   251  }
   252  
   253  // IsSubDomain checks if child is indeed a child of the parent. If child and parent
   254  // are the same domain true is returned as well.
   255  func IsSubDomain(parent, child string) bool {
   256  	// Entire child is contained in parent
   257  	return CompareDomainName(parent, child) == CountLabel(parent)
   258  }
   259  
   260  // IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
   261  // The checking is performed on the binary payload.
   262  func IsMsg(buf []byte) error {
   263  	// Header
   264  	if len(buf) < headerSize {
   265  		return errors.New("dns: bad message header")
   266  	}
   267  	// Header: Opcode
   268  	// TODO(miek): more checks here, e.g. check all header bits.
   269  	return nil
   270  }
   271  
   272  // IsFqdn checks if a domain name is fully qualified.
   273  func IsFqdn(s string) bool {
   274  	// Check for (and remove) a trailing dot, returning if there isn't one.
   275  	if s == "" || s[len(s)-1] != '.' {
   276  		return false
   277  	}
   278  	s = s[:len(s)-1]
   279  
   280  	// If we don't have an escape sequence before the final dot, we know it's
   281  	// fully qualified and can return here.
   282  	if s == "" || s[len(s)-1] != '\\' {
   283  		return true
   284  	}
   285  
   286  	// Otherwise we have to check if the dot is escaped or not by checking if
   287  	// there are an odd or even number of escape sequences before the dot.
   288  	i := strings.LastIndexFunc(s, func(r rune) bool {
   289  		return r != '\\'
   290  	})
   291  	return (len(s)-i)%2 != 0
   292  }
   293  
   294  // IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181.
   295  // This means the RRs need to have the same type, name, and class.
   296  func IsRRset(rrset []RR) bool {
   297  	if len(rrset) == 0 {
   298  		return false
   299  	}
   300  
   301  	baseH := rrset[0].Header()
   302  	for _, rr := range rrset[1:] {
   303  		curH := rr.Header()
   304  		if curH.Rrtype != baseH.Rrtype || curH.Class != baseH.Class || curH.Name != baseH.Name {
   305  			// Mismatch between the records, so this is not a valid rrset for
   306  			// signing/verifying
   307  			return false
   308  		}
   309  	}
   310  
   311  	return true
   312  }
   313  
   314  // Fqdn return the fully qualified domain name from s.
   315  // If s is already fully qualified, it behaves as the identity function.
   316  func Fqdn(s string) string {
   317  	if IsFqdn(s) {
   318  		return s
   319  	}
   320  	return s + "."
   321  }
   322  
   323  // CanonicalName returns the domain name in canonical form. A name in canonical
   324  // form is lowercase and fully qualified. Only US-ASCII letters are affected. See
   325  // Section 6.2 in RFC 4034.
   326  func CanonicalName(s string) string {
   327  	return strings.Map(func(r rune) rune {
   328  		if r >= 'A' && r <= 'Z' {
   329  			r += 'a' - 'A'
   330  		}
   331  		return r
   332  	}, Fqdn(s))
   333  }
   334  
   335  // Copied from the official Go code.
   336  
   337  // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
   338  // address suitable for reverse DNS (PTR) record lookups or an error if it fails
   339  // to parse the IP address.
   340  func ReverseAddr(addr string) (arpa string, err error) {
   341  	ip := net.ParseIP(addr)
   342  	if ip == nil {
   343  		return "", &Error{err: "unrecognized address: " + addr}
   344  	}
   345  	if v4 := ip.To4(); v4 != nil {
   346  		buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa."))
   347  		// Add it, in reverse, to the buffer
   348  		for i := len(v4) - 1; i >= 0; i-- {
   349  			buf = strconv.AppendInt(buf, int64(v4[i]), 10)
   350  			buf = append(buf, '.')
   351  		}
   352  		// Append "in-addr.arpa." and return (buf already has the final .)
   353  		buf = append(buf, "in-addr.arpa."...)
   354  		return string(buf), nil
   355  	}
   356  	// Must be IPv6
   357  	buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa."))
   358  	// Add it, in reverse, to the buffer
   359  	for i := len(ip) - 1; i >= 0; i-- {
   360  		v := ip[i]
   361  		buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.')
   362  	}
   363  	// Append "ip6.arpa." and return (buf already has the final .)
   364  	buf = append(buf, "ip6.arpa."...)
   365  	return string(buf), nil
   366  }
   367  
   368  // String returns the string representation for the type t.
   369  func (t Type) String() string {
   370  	if t1, ok := TypeToString[uint16(t)]; ok {
   371  		return t1
   372  	}
   373  	return "TYPE" + strconv.Itoa(int(t))
   374  }
   375  
   376  // String returns the string representation for the class c.
   377  func (c Class) String() string {
   378  	if s, ok := ClassToString[uint16(c)]; ok {
   379  		// Only emit mnemonics when they are unambiguous, specially ANY is in both.
   380  		if _, ok := StringToType[s]; !ok {
   381  			return s
   382  		}
   383  	}
   384  	return "CLASS" + strconv.Itoa(int(c))
   385  }
   386  
   387  // String returns the string representation for the name n.
   388  func (n Name) String() string {
   389  	return sprintName(string(n))
   390  }
   391  

View as plain text