func (co *Conn) WriteMsg(m *Msg) (err error) {
	var out []byte
	if t := m.IsTsig(); t != nil {
		mac := ""
		if co.TsigProvider != nil {
			out, mac, err = tsigGenerateProvider(m, co.TsigProvider, co.tsigRequestMAC, false)
		} else {
			if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
				return ErrSecret
			}
			out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
		}
		co.tsigRequestMAC = mac
	} else {
		out, err = m.Pack()
	}
	if err != nil {
		return err
	}
	_, err = co.Write(out)
	return err
}
WriteMsg sends a message through the connection co.
If the message m contains a TSIG record the transaction
signature is calculated.
package dns
import (
	"context"
	"crypto/tls"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"strings"
	"time"
)
const (
	dnsTimeout     time.Duration = 2 * time.Second
	tcpIdleTimeout time.Duration = 8 * time.Second
)
type Conn struct {
	net.Conn                          
	UDPSize        uint16             
	TsigSecret     map[string]string  
	TsigProvider   TsigProvider       
	tsigRequestMAC string
}
type Client struct {
	Net       string       
	UDPSize   uint16       
	TLSConfig *tls.Config  
	Dialer    *net.Dialer  
	Timeout        time.Duration
	DialTimeout    time.Duration      
	ReadTimeout    time.Duration      
	WriteTimeout   time.Duration      
	TsigSecret     map[string]string  
	TsigProvider   TsigProvider       
	SingleInflight bool               
	group          singleflight
}
func Exchange(m *Msg, a string) (r *Msg, err error) {
	client := Client{Net: "udp"}
	r, _, err = client.Exchange(m, a)
	return r, err
}
func (c *Client) dialTimeout() time.Duration {
	if c.Timeout != 0 {
		return c.Timeout
	}
	if c.DialTimeout != 0 {
		return c.DialTimeout
	}
	return dnsTimeout
}
func (c *Client) readTimeout() time.Duration {
	if c.ReadTimeout != 0 {
		return c.ReadTimeout
	}
	return dnsTimeout
}
func (c *Client) writeTimeout() time.Duration {
	if c.WriteTimeout != 0 {
		return c.WriteTimeout
	}
	return dnsTimeout
}
func (c *Client) Dial(address string) (conn *Conn, err error) {
	var d net.Dialer
	if c.Dialer == nil {
		d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())}
	} else {
		d = *c.Dialer
	}
	network := c.Net
	if network == "" {
		network = "udp"
	}
	useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls")
	conn = new(Conn)
	if useTLS {
		network = strings.TrimSuffix(network, "-tls")
		conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
	} else {
		conn.Conn, err = d.Dial(network, address)
	}
	if err != nil {
		return nil, err
	}
	conn.UDPSize = c.UDPSize
	return conn, nil
}
func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) {
	co, err := c.Dial(address)
	if err != nil {
		return nil, 0, err
	}
	defer co.Close()
	return c.ExchangeWithConn(m, co)
}
func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) {
	if !c.SingleInflight {
		return c.exchange(m, conn)
	}
	q := m.Question[0]
	key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass)
	r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) {
		return c.exchange(m, conn)
	})
	if r != nil && shared {
		r = r.Copy()
	}
	return r, rtt, err
}
func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {
	opt := m.IsEdns0()
	if opt != nil && opt.UDPSize() >= MinMsgSize {
		co.UDPSize = opt.UDPSize()
	}
	if opt == nil && c.UDPSize >= MinMsgSize {
		co.UDPSize = c.UDPSize
	}
	co.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider
	t := time.Now()
	co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout())))
	if err = co.WriteMsg(m); err != nil {
		return nil, 0, err
	}
	co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout())))
	if _, ok := co.Conn.(net.PacketConn); ok {
		for {
			r, err = co.ReadMsg()
			if err != nil || r.Id == m.Id {
				break
			}
		}
	} else {
		r, err = co.ReadMsg()
		if err == nil && r.Id != m.Id {
			err = ErrId
		}
	}
	rtt = time.Since(t)
	return r, rtt, err
}
func (co *Conn) ReadMsg() (*Msg, error) {
	p, err := co.ReadMsgHeader(nil)
	if err != nil {
		return nil, err
	}
	m := new(Msg)
	if err := m.Unpack(p); err != nil {
		return m, err
	}
	if t := m.IsTsig(); t != nil {
		if co.TsigProvider != nil {
			err = tsigVerifyProvider(p, co.TsigProvider, co.tsigRequestMAC, false)
		} else {
			if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
				return m, ErrSecret
			}
			err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
		}
	}
	return m, err
}
func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
	var (
		p   []byte
		n   int
		err error
	)
	if _, ok := co.Conn.(net.PacketConn); ok {
		if co.UDPSize > MinMsgSize {
			p = make([]byte, co.UDPSize)
		} else {
			p = make([]byte, MinMsgSize)
		}
		n, err = co.Read(p)
	} else {
		var length uint16
		if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
			return nil, err
		}
		p = make([]byte, length)
		n, err = io.ReadFull(co.Conn, p)
	}
	if err != nil {
		return nil, err
	} else if n < headerSize {
		return nil, ErrShortRead
	}
	p = p[:n]
	if hdr != nil {
		dh, _, err := unpackMsgHdr(p, 0)
		if err != nil {
			return nil, err
		}
		*hdr = dh
	}
	return p, err
}
func (co *Conn) Read(p []byte) (n int, err error) {
	if co.Conn == nil {
		return 0, ErrConnEmpty
	}
	if _, ok := co.Conn.(net.PacketConn); ok {
		return co.Conn.Read(p)
	}
	var length uint16
	if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil {
		return 0, err
	}
	if int(length) > len(p) {
		return 0, io.ErrShortBuffer
	}
	return io.ReadFull(co.Conn, p[:length])
}