2011-04-13 05:44:56 +10:00
package dns
2013-01-29 06:41:17 +11:00
// A client implementation.
2011-04-13 05:44:56 +10:00
import (
2017-06-30 21:44:44 +10:00
"context"
2016-01-04 23:05:29 +11:00
"crypto/tls"
2016-06-09 01:38:42 +10:00
"encoding/binary"
2019-04-10 20:55:21 +10:00
"fmt"
2011-04-16 07:55:27 +10:00
"io"
"net"
2017-09-29 19:52:01 +10:00
"strings"
2012-01-20 22:13:47 +11:00
"time"
2011-04-13 05:44:56 +10:00
)
2018-05-21 01:56:13 +10:00
const (
dnsTimeout time . Duration = 2 * time . Second
tcpIdleTimeout time . Duration = 8 * time . Second
)
2018-05-16 17:54:01 +10:00
2013-10-12 08:34:04 +11:00
// A Conn represents a connection to a DNS server.
2013-09-29 05:31:29 +10:00
type Conn struct {
2013-10-12 03:18:37 +11:00
net . Conn // a net.Conn holding the connection
2014-07-31 18:48:26 +10:00
UDPSize uint16 // minimum receive buffer for UDP messages
2017-11-18 00:17:47 +11:00
TsigSecret map [ string ] string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
2021-01-08 01:28:20 +11:00
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
2013-10-12 03:18:37 +11:00
tsigRequestMAC string
2011-04-18 05:56:40 +10:00
}
2013-11-22 00:27:20 +11:00
// A Client defines parameters for a DNS client.
2011-04-13 05:44:56 +10:00
type Client struct {
2017-09-29 19:52:01 +10:00
Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
UDPSize uint16 // minimum receive buffer for UDP messages
TLSConfig * tls . Config // TLS connection configuration
Dialer * net . Dialer // a net.Dialer used to set local address, timeouts and more
// Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout,
// WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and
2020-08-26 15:58:07 +10:00
// Client.Dialer) or context.Context.Deadline (see ExchangeContext)
2017-09-29 19:52:01 +10:00
Timeout time . Duration
DialTimeout time . Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
2016-04-19 20:29:51 +10:00
ReadTimeout time . Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time . Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
2017-11-18 00:17:47 +11:00
TsigSecret map [ string ] string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
2021-01-08 01:28:20 +11:00
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
2013-09-06 19:49:07 +10:00
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
group singleflight
2013-08-24 08:56:34 +10:00
}
2013-09-29 19:22:27 +10:00
// Exchange performs a synchronous UDP query. It sends the message m to the address
2016-10-04 01:10:00 +11:00
// contained in a and waits for a reply. Exchange does not retry a failed query, nor
2013-12-31 03:46:53 +11:00
// will it fall back to TCP in case of truncation.
2016-03-02 20:40:40 +11:00
// See client.Exchange for more information on setting larger buffer sizes.
2013-09-29 06:58:08 +10:00
func Exchange ( m * Msg , a string ) ( r * Msg , err error ) {
2017-09-29 19:52:01 +10:00
client := Client { Net : "udp" }
r , _ , err = client . Exchange ( m , a )
return r , err
}
2015-05-06 16:20:46 +10:00
2017-09-29 19:52:01 +10:00
func ( c * Client ) dialTimeout ( ) time . Duration {
if c . Timeout != 0 {
return c . Timeout
2015-05-06 16:20:46 +10:00
}
2017-09-29 19:52:01 +10:00
if c . DialTimeout != 0 {
return c . DialTimeout
2015-05-06 16:20:46 +10:00
}
2017-09-29 19:52:01 +10:00
return dnsTimeout
}
2015-10-06 22:30:01 +11:00
2017-09-29 19:52:01 +10:00
func ( c * Client ) readTimeout ( ) time . Duration {
if c . ReadTimeout != 0 {
return c . ReadTimeout
2015-05-06 16:20:46 +10:00
}
2017-09-29 19:52:01 +10:00
return dnsTimeout
2013-09-29 05:31:29 +10:00
}
2017-09-29 19:52:01 +10:00
func ( c * Client ) writeTimeout ( ) time . Duration {
if c . WriteTimeout != 0 {
return c . WriteTimeout
2017-06-30 21:44:44 +10:00
}
2017-09-29 19:52:01 +10:00
return dnsTimeout
}
2017-06-30 21:44:44 +10:00
2017-11-04 03:15:35 +11:00
// Dial connects to the address on the named network.
2017-09-29 19:52:01 +10:00
func ( c * Client ) Dial ( address string ) ( conn * Conn , err error ) {
// create a new dialer with the appropriate timeout
var d net . Dialer
if c . Dialer == nil {
2018-08-17 02:05:27 +10:00
d = net . Dialer { Timeout : c . getTimeoutForRequest ( c . dialTimeout ( ) ) }
2017-09-29 19:52:01 +10:00
} else {
2018-09-09 02:27:21 +10:00
d = * c . Dialer
2017-06-30 21:44:44 +10:00
}
2018-09-09 02:27:21 +10:00
network := c . Net
if network == "" {
network = "udp"
2017-06-30 21:44:44 +10:00
}
2018-09-09 02:27:21 +10:00
useTLS := strings . HasPrefix ( network , "tcp" ) && strings . HasSuffix ( network , "-tls" )
2017-09-29 19:52:01 +10:00
conn = new ( Conn )
if useTLS {
2018-09-09 02:27:21 +10:00
network = strings . TrimSuffix ( network , "-tls" )
2017-09-29 19:52:01 +10:00
conn . Conn , err = tls . DialWithDialer ( & d , network , address , c . TLSConfig )
} else {
conn . Conn , err = d . Dial ( network , address )
2013-10-12 22:35:09 +11:00
}
2017-09-29 19:52:01 +10:00
if err != nil {
return nil , err
2015-05-06 15:56:42 +10:00
}
2020-10-11 18:06:33 +11:00
conn . UDPSize = c . UDPSize
2017-09-29 19:52:01 +10:00
return conn , nil
2013-10-12 22:35:09 +11:00
}
2016-10-04 01:10:00 +11:00
// Exchange performs a synchronous query. It sends the message m to the address
// contained in a and waits for a reply. Basic use pattern with a *dns.Client:
2013-09-29 06:58:08 +10:00
//
// c := new(dns.Client)
// in, rtt, err := c.Exchange(message, "127.0.0.1:53")
//
2014-01-04 19:30:08 +11:00
// Exchange does not retry a failed query, nor will it fall back to TCP in
2013-12-27 18:48:30 +11:00
// case of truncation.
2016-03-02 20:40:40 +11:00
// It is up to the caller to create a message that allows for larger responses to be
// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger
2017-03-20 02:10:46 +11:00
// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit
2017-09-29 19:52:01 +10:00
// of 512 bytes
// To specify a local address or a timeout, the caller has to set the `Client.Dialer`
// attribute appropriately
func ( c * Client ) Exchange ( m * Msg , address string ) ( r * Msg , rtt time . Duration , err error ) {
2020-05-04 18:22:21 +10:00
co , err := c . Dial ( address )
if err != nil {
return nil , 0 , err
}
defer co . Close ( )
return c . ExchangeWithConn ( m , co )
}
// ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection
// that will be used instead of creating a new one.
// Usage pattern with a *dns.Client:
// c := new(dns.Client)
// // connection management logic goes here
//
// conn := c.Dial(address)
// in, rtt, err := c.ExchangeWithConn(message, conn)
//
// This allows users of the library to implement their own connection management,
// as opposed to Exchange, which will always use new connections and incur the added overhead
// that entails when using "tcp" and especially "tcp-tls" clients.
func ( c * Client ) ExchangeWithConn ( m * Msg , conn * Conn ) ( r * Msg , rtt time . Duration , err error ) {
2013-09-06 19:49:07 +10:00
if ! c . SingleInflight {
2020-05-04 18:22:21 +10:00
return c . exchange ( m , conn )
2013-08-24 08:56:34 +10:00
}
2017-09-29 19:52:01 +10:00
2019-04-10 20:55:21 +10:00
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 ) {
2020-05-04 18:22:21 +10:00
return c . exchange ( m , conn )
2013-08-24 08:56:34 +10:00
} )
2017-02-17 22:38:00 +11:00
if r != nil && shared {
r = r . Copy ( )
}
2019-04-10 20:55:21 +10:00
2017-09-29 19:52:01 +10:00
return r , rtt , err
2015-05-06 16:23:53 +10:00
}
2020-05-04 18:22:21 +10:00
func ( c * Client ) exchange ( m * Msg , co * Conn ) ( r * Msg , rtt time . Duration , err error ) {
2015-05-06 16:23:53 +10:00
2013-09-29 20:21:18 +10:00
opt := m . IsEdns0 ( )
2014-07-31 18:48:26 +10:00
// If EDNS0 is used use that for size.
2013-09-29 20:21:18 +10:00
if opt != nil && opt . UDPSize ( ) >= MinMsgSize {
co . UDPSize = opt . UDPSize ( )
}
2014-07-31 18:48:26 +10:00
// Otherwise use the client's configured UDP size.
2014-02-15 09:14:41 +11:00
if opt == nil && c . UDPSize >= MinMsgSize {
co . UDPSize = c . UDPSize
}
2015-05-06 16:23:53 +10:00
2021-01-08 01:28:20 +11:00
co . TsigSecret , co . TsigProvider = c . TsigSecret , c . TsigProvider
2018-03-27 18:01:29 +11:00
t := time . Now ( )
2017-09-29 19:52:01 +10:00
// write with the appropriate write timeout
2018-03-27 18:01:29 +11:00
co . SetWriteDeadline ( t . Add ( c . getTimeoutForRequest ( c . writeTimeout ( ) ) ) )
2013-09-29 20:21:18 +10:00
if err = co . WriteMsg ( m ) ; err != nil {
2013-01-29 06:30:13 +11:00
return nil , 0 , err
}
2015-10-06 22:30:01 +11:00
2017-09-29 19:52:01 +10:00
co . SetReadDeadline ( time . Now ( ) . Add ( c . getTimeoutForRequest ( c . readTimeout ( ) ) ) )
Ignore responses with unexpected IDs (#1155)
* Ignore replies with unexpected IDs
This fixes the following problem:
At time 0, we send a query with ID X from port P.
At time T, we time out the query due to lack of response, and then send
a different query with ID Y. By coincidence, the new query is sent from
the same port number P (since port numbers are only 16 bits, this can happen
with non-negligible probability when making queries at a high rate).
At time T+epsilon, we receive a response to the original query.
Since the ID in this response is X, not Y, we would previously return
ErrId, preventing the second query from succeeding.
With this commit, we simply ignore the response with the mismatched ID
and return once we receive the response with the correct ID.
* Update test for bad ID
The new test sends two replies: the first one has a bad ID, which should
be ignored, and the second one has the correct ID.
* Add test to ensure query times out when server returns bad ID
* Avoid use of error string matching in test case
* Check for mismatched query IDs when using TCP
* Reduce timeout in TestClientSyncBadID
2020-10-18 16:55:24 +11:00
if _ , ok := co . Conn . ( net . PacketConn ) ; ok {
for {
r , err = co . ReadMsg ( )
// Ignore replies with mismatched IDs because they might be
// responses to earlier queries that timed out.
if err != nil || r . Id == m . Id {
break
}
}
} else {
r , err = co . ReadMsg ( )
if err == nil && r . Id != m . Id {
err = ErrId
}
2015-05-06 15:56:42 +10:00
}
2018-03-27 18:01:29 +11:00
rtt = time . Since ( t )
return r , rtt , err
2011-08-08 21:10:35 +10:00
}
2013-09-29 20:21:18 +10:00
// ReadMsg reads a message from the connection co.
2017-12-19 07:15:56 +11:00
// If the received message contains a TSIG record the transaction signature
// is verified. This method always tries to return the message, however if an
// error is returned there are no guarantees that the returned message is a
// valid representation of the packet read.
2013-09-29 20:21:18 +10:00
func ( co * Conn ) ReadMsg ( ) ( * Msg , error ) {
2015-07-03 18:31:53 +10:00
p , err := co . ReadMsgHeader ( nil )
2015-06-25 05:09:46 +10:00
if err != nil {
2011-04-19 06:08:12 +10:00
return nil , err
}
2015-06-25 05:09:46 +10:00
2015-06-29 10:20:16 +10:00
m := new ( Msg )
2012-10-10 06:17:54 +11:00
if err := m . Unpack ( p ) ; err != nil {
2017-12-19 07:15:56 +11:00
// If an error was returned, we still want to allow the user to use
2015-11-02 07:46:57 +11:00
// the message, but naively they can just check err if they don't want
2017-12-19 07:15:56 +11:00
// to use an erroneous message
return m , err
2011-04-19 06:08:12 +10:00
}
2012-08-25 19:24:01 +10:00
if t := m . IsTsig ( ) ; t != nil {
2021-01-08 01:28:20 +11:00
if co . TsigProvider != nil {
err = tsigVerifyProvider ( p , co . TsigProvider , co . tsigRequestMAC , false )
} else {
if _ , ok := co . TsigSecret [ t . Hdr . Name ] ; ! ok {
return m , ErrSecret
}
// Need to work on the original message p, as that was used to calculate the tsig.
err = TsigVerify ( p , co . TsigSecret [ t . Hdr . Name ] , co . tsigRequestMAC , false )
2011-04-23 00:37:26 +10:00
}
}
2013-09-29 19:22:27 +10:00
return m , err
2011-04-18 05:56:40 +10:00
}
2011-04-16 07:55:27 +10:00
2015-07-03 18:31:53 +10:00
// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
// Returns message as a byte slice to be parsed with Msg.Unpack later on.
2015-07-03 12:39:49 +10:00
// Note that error handling on the message body is not possible as only the header is parsed.
2015-07-03 18:31:53 +10:00
func ( co * Conn ) ReadMsgHeader ( hdr * Header ) ( [ ] byte , error ) {
2015-06-29 22:06:49 +10:00
var (
p [ ] byte
n int
err error
)
2019-03-11 21:59:25 +11:00
2019-05-22 23:38:57 +10:00
if _ , ok := co . Conn . ( net . PacketConn ) ; ok {
2015-06-25 05:09:46 +10:00
if co . UDPSize > MinMsgSize {
p = make ( [ ] byte , co . UDPSize )
} else {
p = make ( [ ] byte , MinMsgSize )
}
2015-06-29 22:06:49 +10:00
n , err = co . Read ( p )
2019-05-22 23:38:57 +10:00
} 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 )
2015-06-25 05:09:46 +10:00
}
if err != nil {
return nil , err
2015-07-03 18:31:53 +10:00
} else if n < headerSize {
2015-06-25 05:09:46 +10:00
return nil , ErrShortRead
}
p = p [ : n ]
if hdr != nil {
2016-06-13 06:06:46 +10:00
dh , _ , err := unpackMsgHdr ( p , 0 )
if err != nil {
2015-06-25 05:09:46 +10:00
return nil , err
}
2016-06-13 06:06:46 +10:00
* hdr = dh
2015-06-25 05:09:46 +10:00
}
return p , err
}
2013-09-29 20:21:18 +10:00
// Read implements the net.Conn read method.
2013-09-29 06:58:08 +10:00
func ( co * Conn ) Read ( p [ ] byte ) ( n int , err error ) {
if co . Conn == nil {
2011-11-03 09:06:54 +11:00
return 0 , ErrConnEmpty
2011-04-18 05:56:40 +10:00
}
2016-01-08 02:27:07 +11:00
2019-05-22 23:38:57 +10:00
if _ , ok := co . Conn . ( net . PacketConn ) ; ok {
// UDP connection
return co . Conn . Read ( p )
}
2019-03-11 21:59:25 +11:00
2019-05-22 23:38:57 +10:00
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
2013-10-13 04:32:53 +11:00
}
2019-03-11 21:59:25 +11:00
2019-05-22 23:38:57 +10:00
return io . ReadFull ( co . Conn , p [ : length ] )
2011-04-16 07:55:27 +10:00
}
2016-01-20 09:24:18 +11:00
// WriteMsg sends a message through the connection co.
2011-04-19 06:08:12 +10:00
// If the message m contains a TSIG record the transaction
// signature is calculated.
2013-09-29 20:21:18 +10:00
func ( co * Conn ) WriteMsg ( m * Msg ) ( err error ) {
2012-03-03 07:19:37 +11:00
var out [ ] byte
2012-08-25 19:24:01 +10:00
if t := m . IsTsig ( ) ; t != nil {
2012-03-03 07:19:37 +11:00
mac := ""
2021-01-08 01:28:20 +11:00
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 )
2011-04-23 00:37:26 +10:00
}
2016-01-20 09:23:11 +11:00
// Set for the next read, although only used in zone transfers
2013-10-12 03:18:37 +11:00
co . tsigRequestMAC = mac
2012-03-03 07:19:37 +11:00
} else {
2012-10-10 06:17:54 +11:00
out , err = m . Pack ( )
2012-10-16 05:00:49 +11:00
}
if err != nil {
return err
2011-04-16 07:55:27 +10:00
}
2019-01-04 21:19:42 +11:00
_ , err = co . Write ( out )
return err
2011-04-16 07:55:27 +10:00
}
2013-09-29 20:21:18 +10:00
// Write implements the net.Conn Write method.
2019-05-22 23:38:57 +10:00
func ( co * Conn ) Write ( p [ ] byte ) ( int , error ) {
if len ( p ) > MaxMsgSize {
return 0 , & Error { err : "message too large" }
}
2019-03-11 00:46:14 +11:00
2019-05-22 23:38:57 +10:00
if _ , ok := co . Conn . ( net . PacketConn ) ; ok {
return co . Conn . Write ( p )
2011-04-16 07:55:27 +10:00
}
2019-03-11 00:46:14 +11:00
2021-02-14 05:49:02 +11:00
msg := make ( [ ] byte , 2 + len ( p ) )
binary . BigEndian . PutUint16 ( msg , uint16 ( len ( p ) ) )
copy ( msg [ 2 : ] , p )
return co . Conn . Write ( msg )
2011-04-16 07:55:27 +10:00
}
2012-05-05 07:18:29 +10:00
2017-09-29 19:52:01 +10:00
// Return the appropriate timeout for a specific request
func ( c * Client ) getTimeoutForRequest ( timeout time . Duration ) time . Duration {
var requestTimeout time . Duration
if c . Timeout != 0 {
requestTimeout = c . Timeout
} else {
requestTimeout = timeout
}
// net.Dialer.Timeout has priority if smaller than the timeouts computed so
// far
if c . Dialer != nil && c . Dialer . Timeout != 0 {
if c . Dialer . Timeout < requestTimeout {
requestTimeout = c . Dialer . Timeout
}
}
return requestTimeout
}
2013-11-01 20:40:44 +11:00
// Dial connects to the address on the named network.
func Dial ( network , address string ) ( conn * Conn , err error ) {
2013-11-01 20:50:55 +11:00
conn = new ( Conn )
2013-11-01 20:40:44 +11:00
conn . Conn , err = net . Dial ( network , address )
if err != nil {
return nil , err
}
return conn , nil
}
2017-09-29 19:52:01 +10:00
// ExchangeContext performs a synchronous UDP query, like Exchange. It
// additionally obeys deadlines from the passed Context.
func ExchangeContext ( ctx context . Context , m * Msg , a string ) ( r * Msg , err error ) {
client := Client { Net : "udp" }
r , _ , err = client . ExchangeContext ( ctx , m , a )
2021-02-26 03:08:05 +11:00
// ignoring rtt to leave the original ExchangeContext API unchanged, but
2017-09-29 19:52:01 +10:00
// this function will go away
return r , err
}
// ExchangeConn performs a synchronous query. It sends the message m via the connection
// c and waits for a reply. The connection c is not closed by ExchangeConn.
2019-02-07 18:30:09 +11:00
// Deprecated: This function is going away, but can easily be mimicked:
2017-09-29 19:52:01 +10:00
//
// co := &dns.Conn{Conn: c} // c is your net.Conn
// co.WriteMsg(m)
// in, _ := co.ReadMsg()
// co.Close()
//
func ExchangeConn ( c net . Conn , m * Msg ) ( r * Msg , err error ) {
println ( "dns: ExchangeConn: this function is deprecated" )
co := new ( Conn )
co . Conn = c
if err = co . WriteMsg ( m ) ; err != nil {
return nil , err
}
r , err = co . ReadMsg ( )
if err == nil && r . Id != m . Id {
err = ErrId
}
return r , err
}
2015-02-19 20:58:33 +11:00
// DialTimeout acts like Dial but takes a timeout.
2013-11-01 20:40:44 +11:00
func DialTimeout ( network , address string , timeout time . Duration ) ( conn * Conn , err error ) {
2017-10-01 05:54:08 +11:00
client := Client { Net : network , Dialer : & net . Dialer { Timeout : timeout } }
2019-01-04 21:19:42 +11:00
return client . Dial ( address )
2013-11-01 20:40:44 +11:00
}
2016-01-04 23:05:29 +11:00
// DialWithTLS connects to the address on the named network with TLS.
2016-01-04 23:30:39 +11:00
func DialWithTLS ( network , address string , tlsConfig * tls . Config ) ( conn * Conn , err error ) {
2017-09-29 19:52:01 +10:00
if ! strings . HasSuffix ( network , "-tls" ) {
network += "-tls"
}
client := Client { Net : network , TLSConfig : tlsConfig }
2019-01-04 21:19:42 +11:00
return client . Dial ( address )
2016-01-04 23:05:29 +11:00
}
// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout.
2016-01-04 23:30:39 +11:00
func DialTimeoutWithTLS ( network , address string , tlsConfig * tls . Config , timeout time . Duration ) ( conn * Conn , err error ) {
2017-09-29 19:52:01 +10:00
if ! strings . HasSuffix ( network , "-tls" ) {
network += "-tls"
}
client := Client { Net : network , Dialer : & net . Dialer { Timeout : timeout } , TLSConfig : tlsConfig }
2019-01-04 21:19:42 +11:00
return client . Dial ( address )
2016-01-04 23:05:29 +11:00
}
2016-04-19 20:29:51 +10:00
2017-09-29 19:52:01 +10:00
// ExchangeContext acts like Exchange, but honors the deadline on the provided
// context, if present. If there is both a context deadline and a configured
// timeout on the client, the earliest of the two takes effect.
func ( c * Client ) ExchangeContext ( ctx context . Context , m * Msg , a string ) ( r * Msg , rtt time . Duration , err error ) {
var timeout time . Duration
if deadline , ok := ctx . Deadline ( ) ; ! ok {
timeout = 0
} else {
Fix dominikh/go-tools nits (#758)
* Remove unused functions and consts
* Address gosimple nits
* Address staticcheck nits
This excludes several that were intentional or weren't actual errors.
* Reduce size of lex struct
This reduces the size of the lex struct by 8 bytes from:
lex.token string: 0-16 (size 16, align 8)
lex.tokenUpper string: 16-32 (size 16, align 8)
lex.length int: 32-40 (size 8, align 8)
lex.err bool: 40-41 (size 1, align 1)
lex.value uint8: 41-42 (size 1, align 1)
padding: 42-48 (size 6, align 0)
lex.line int: 48-56 (size 8, align 8)
lex.column int: 56-64 (size 8, align 8)
lex.torc uint16: 64-66 (size 2, align 2)
padding: 66-72 (size 6, align 0)
lex.comment string: 72-88 (size 16, align 8)
to:
lex.token string: 0-16 (size 16, align 8)
lex.tokenUpper string: 16-32 (size 16, align 8)
lex.length int: 32-40 (size 8, align 8)
lex.err bool: 40-41 (size 1, align 1)
lex.value uint8: 41-42 (size 1, align 1)
lex.torc uint16: 42-44 (size 2, align 2)
padding: 44-48 (size 4, align 0)
lex.line int: 48-56 (size 8, align 8)
lex.column int: 56-64 (size 8, align 8)
lex.comment string: 64-80 (size 16, align 8)
* Reduce size of response struct
This reduces the size of the response struct by 8 bytes from:
response.msg []byte: 0-24 (size 24, align 8)
response.hijacked bool: 24-25 (size 1, align 1)
padding: 25-32 (size 7, align 0)
response.tsigStatus error: 32-48 (size 16, align 8)
response.tsigTimersOnly bool: 48-49 (size 1, align 1)
padding: 49-56 (size 7, align 0)
response.tsigRequestMAC string: 56-72 (size 16, align 8)
response.tsigSecret map[string]string: 72-80 (size 8, align 8)
response.udp *net.UDPConn: 80-88 (size 8, align 8)
response.tcp net.Conn: 88-104 (size 16, align 8)
response.udpSession *github.com/tmthrgd/dns.SessionUDP: 104-112 (size 8, align 8)
response.writer github.com/tmthrgd/dns.Writer: 112-128 (size 16, align 8)
response.wg *sync.WaitGroup: 128-136 (size 8, align 8)
to:
response.msg []byte: 0-24 (size 24, align 8)
response.hijacked bool: 24-25 (size 1, align 1)
response.tsigTimersOnly bool: 25-26 (size 1, align 1)
padding: 26-32 (size 6, align 0)
response.tsigStatus error: 32-48 (size 16, align 8)
response.tsigRequestMAC string: 48-64 (size 16, align 8)
response.tsigSecret map[string]string: 64-72 (size 8, align 8)
response.udp *net.UDPConn: 72-80 (size 8, align 8)
response.tcp net.Conn: 80-96 (size 16, align 8)
response.udpSession *github.com/tmthrgd/dns.SessionUDP: 96-104 (size 8, align 8)
response.writer github.com/tmthrgd/dns.Writer: 104-120 (size 16, align 8)
response.wg *sync.WaitGroup: 120-128 (size 8, align 8)
2018-09-27 04:32:05 +10:00
timeout = time . Until ( deadline )
2017-06-30 21:44:44 +10:00
}
2017-09-29 19:52:01 +10:00
// not passing the context to the underlying calls, as the API does not support
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
2018-11-02 07:16:39 +11:00
// TODO(tmthrgd,miekg): this is a race condition.
2017-09-29 19:52:01 +10:00
c . Dialer = & net . Dialer { Timeout : timeout }
return c . Exchange ( m , a )
2017-06-30 21:44:44 +10:00
}