package dns

import (
	
	
	
	
)

const hexDigit = "0123456789abcdef"

// Everything is assumed in ClassINET.

// SetReply creates a reply message from a request message.
func ( *Msg) ( *Msg) *Msg {
	.Id = .Id
	.Response = true
	.Opcode = .Opcode
	if .Opcode == OpcodeQuery {
		.RecursionDesired = .RecursionDesired // Copy rd bit
		.CheckingDisabled = .CheckingDisabled // Copy cd bit
	}
	.Rcode = RcodeSuccess
	if len(.Question) > 0 {
		.Question = []Question{.Question[0]}
	}
	return 
}

// SetQuestion creates a question message, it sets the Question
// section, generates an Id and sets the RecursionDesired (RD)
// bit to true.
func ( *Msg) ( string,  uint16) *Msg {
	.Id = Id()
	.RecursionDesired = true
	.Question = make([]Question, 1)
	.Question[0] = Question{, , ClassINET}
	return 
}

// SetNotify creates a notify message, it sets the Question
// section, generates an Id and sets the Authoritative (AA)
// bit to true.
func ( *Msg) ( string) *Msg {
	.Opcode = OpcodeNotify
	.Authoritative = true
	.Id = Id()
	.Question = make([]Question, 1)
	.Question[0] = Question{, TypeSOA, ClassINET}
	return 
}

// SetRcode creates an error message suitable for the request.
func ( *Msg) ( *Msg,  int) *Msg {
	.SetReply()
	.Rcode = 
	return 
}

// SetRcodeFormatError creates a message with FormError set.
func ( *Msg) ( *Msg) *Msg {
	.Rcode = RcodeFormatError
	.Opcode = OpcodeQuery
	.Response = true
	.Authoritative = false
	.Id = .Id
	return 
}

// SetUpdate makes the message a dynamic update message. It
// sets the ZONE section to: z, TypeSOA, ClassINET.
func ( *Msg) ( string) *Msg {
	.Id = Id()
	.Response = false
	.Opcode = OpcodeUpdate
	.Compress = false // BIND9 cannot handle compression
	.Question = make([]Question, 1)
	.Question[0] = Question{, TypeSOA, ClassINET}
	return 
}

// SetIxfr creates message for requesting an IXFR.
func ( *Msg) ( string,  uint32, ,  string) *Msg {
	.Id = Id()
	.Question = make([]Question, 1)
	.Ns = make([]RR, 1)
	 := new(SOA)
	.Hdr = RR_Header{, TypeSOA, ClassINET, defaultTtl, 0}
	.Serial = 
	.Ns = 
	.Mbox = 
	.Question[0] = Question{, TypeIXFR, ClassINET}
	.Ns[0] = 
	return 
}

// SetAxfr creates message for requesting an AXFR.
func ( *Msg) ( string) *Msg {
	.Id = Id()
	.Question = make([]Question, 1)
	.Question[0] = Question{, TypeAXFR, ClassINET}
	return 
}

// SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The TSIG is calculated when the message is being send.
func ( *Msg) (,  string,  uint16,  int64) *Msg {
	 := new(TSIG)
	.Hdr = RR_Header{, TypeTSIG, ClassANY, 0, 0}
	.Algorithm = 
	.Fudge = 
	.TimeSigned = uint64()
	.OrigId = .Id
	.Extra = append(.Extra, )
	return 
}

// SetEdns0 appends a EDNS0 OPT RR to the message.
// TSIG should always the last RR in a message.
func ( *Msg) ( uint16,  bool) *Msg {
	 := new(OPT)
	.Hdr.Name = "."
	.Hdr.Rrtype = TypeOPT
	.SetUDPSize()
	if  {
		.SetDo()
	}
	.Extra = append(.Extra, )
	return 
}

// IsTsig checks if the message has a TSIG record as the last record
// in the additional section. It returns the TSIG record found or nil.
func ( *Msg) () *TSIG {
	if len(.Extra) > 0 {
		if .Extra[len(.Extra)-1].Header().Rrtype == TypeTSIG {
			return .Extra[len(.Extra)-1].(*TSIG)
		}
	}
	return nil
}

// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
// record in the additional section will do. It returns the OPT record
// found or nil.
func ( *Msg) () *OPT {
	// RFC 6891, Section 6.1.1 allows the OPT record to appear
	// anywhere in the additional record section, but it's usually at
	// the end so start there.
	for  := len(.Extra) - 1;  >= 0; -- {
		if .Extra[].Header().Rrtype == TypeOPT {
			return .Extra[].(*OPT)
		}
	}
	return nil
}

// popEdns0 is like IsEdns0, but it removes the record from the message.
func ( *Msg) () *OPT {
	// RFC 6891, Section 6.1.1 allows the OPT record to appear
	// anywhere in the additional record section, but it's usually at
	// the end so start there.
	for  := len(.Extra) - 1;  >= 0; -- {
		if .Extra[].Header().Rrtype == TypeOPT {
			 := .Extra[].(*OPT)
			.Extra = append(.Extra[:], .Extra[+1:]...)
			return 
		}
	}
	return nil
}

// IsDomainName checks if s is a valid domain name, it returns the number of
// labels and true, when a domain name is valid.  Note that non fully qualified
// domain name is considered valid, in this case the last label is counted in
// the number of labels.  When false is returned the number of labels is not
// defined.  Also note that this function is extremely liberal; almost any
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
// label fits in 63 characters and that the entire name will fit into the 255
// octet wire format limit.
func ( string) ( int,  bool) {
	// XXX: The logic in this function was copied from packDomainName and
	// should be kept in sync with that function.

	const  = 256

	if len() == 0 { // Ok, for instance when dealing with update RR without any rdata.
		return 0, false
	}

	 = Fqdn()

	// Each dot ends a segment of the name. Except for escaped dots (\.), which
	// are normal dots.

	var (
		    int
		  int
		 bool
		 bool
	)
	for  := 0;  < len(); ++ {
		switch [] {
		case '\\':
			 = !
			if +1 >  {
				return , false
			}

			// check for \DDD
			if isDDD([+1:]) {
				 += 3
				 += 3
			} else {
				++
				++
			}

			 = false
		case '.':
			 = false
			if  == 0 && len() > 1 {
				// leading dots are not legal except for the root zone
				return , false
			}

			if  {
				// two dots back to back is not legal
				return , false
			}
			 = true

			 :=  - 
			if  >= 1<<6 { // top two bits of length must be clear
				return , false
			}

			// off can already (we're in a loop) be bigger than lenmsg
			// this happens when a name isn't fully qualified
			 += 1 + 
			if  >  {
				return , false
			}

			++
			 =  + 1
		default:
			 = false
			 = false
		}
	}
	if  {
		return , false
	}
	return , true
}

// IsSubDomain checks if child is indeed a child of the parent. If child and parent
// are the same domain true is returned as well.
func (,  string) bool {
	// Entire child is contained in parent
	return CompareDomainName(, ) == CountLabel()
}

// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
// The checking is performed on the binary payload.
func ( []byte) error {
	// Header
	if len() < headerSize {
		return errors.New("dns: bad message header")
	}
	// Header: Opcode
	// TODO(miek): more checks here, e.g. check all header bits.
	return nil
}

// IsFqdn checks if a domain name is fully qualified.
func ( string) bool {
	// Check for (and remove) a trailing dot, returning if there isn't one.
	if  == "" || [len()-1] != '.' {
		return false
	}
	 = [:len()-1]

	// If we don't have an escape sequence before the final dot, we know it's
	// fully qualified and can return here.
	if  == "" || [len()-1] != '\\' {
		return true
	}

	// Otherwise we have to check if the dot is escaped or not by checking if
	// there are an odd or even number of escape sequences before the dot.
	 := strings.LastIndexFunc(, func( rune) bool {
		return  != '\\'
	})
	return (len()-)%2 != 0
}

// IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181.
// This means the RRs need to have the same type, name, and class.
func ( []RR) bool {
	if len() == 0 {
		return false
	}

	 := [0].Header()
	for ,  := range [1:] {
		 := .Header()
		if .Rrtype != .Rrtype || .Class != .Class || .Name != .Name {
			// Mismatch between the records, so this is not a valid rrset for
			// signing/verifying
			return false
		}
	}

	return true
}

// Fqdn return the fully qualified domain name from s.
// If s is already fully qualified, it behaves as the identity function.
func ( string) string {
	if IsFqdn() {
		return 
	}
	return  + "."
}

// CanonicalName returns the domain name in canonical form. A name in canonical
// form is lowercase and fully qualified. Only US-ASCII letters are affected. See
// Section 6.2 in RFC 4034.
func ( string) string {
	return strings.Map(func( rune) rune {
		if  >= 'A' &&  <= 'Z' {
			 += 'a' - 'A'
		}
		return 
	}, Fqdn())
}

// Copied from the official Go code.

// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address suitable for reverse DNS (PTR) record lookups or an error if it fails
// to parse the IP address.
func ( string) ( string,  error) {
	 := net.ParseIP()
	if  == nil {
		return "", &Error{err: "unrecognized address: " + }
	}
	if  := .To4();  != nil {
		 := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa."))
		// Add it, in reverse, to the buffer
		for  := len() - 1;  >= 0; -- {
			 = strconv.AppendInt(, int64([]), 10)
			 = append(, '.')
		}
		// Append "in-addr.arpa." and return (buf already has the final .)
		 = append(, "in-addr.arpa."...)
		return string(), nil
	}
	// Must be IPv6
	 := make([]byte, 0, net.IPv6len*4+len("ip6.arpa."))
	// Add it, in reverse, to the buffer
	for  := len() - 1;  >= 0; -- {
		 := []
		 = append(, hexDigit[&0xF], '.', hexDigit[>>4], '.')
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	 = append(, "ip6.arpa."...)
	return string(), nil
}

// String returns the string representation for the type t.
func ( Type) () string {
	if ,  := TypeToString[uint16()];  {
		return 
	}
	return "TYPE" + strconv.Itoa(int())
}

// String returns the string representation for the class c.
func ( Class) () string {
	if ,  := ClassToString[uint16()];  {
		// Only emit mnemonics when they are unambiguous, specially ANY is in both.
		if ,  := StringToType[]; ! {
			return 
		}
	}
	return "CLASS" + strconv.Itoa(int())
}

// String returns the string representation for the name n.
func ( Name) () string {
	return sprintName(string())
}