@@ -27,6 +27,20 @@ import (
27
27
"golang_org/x/net/dns/dnsmessage"
28
28
)
29
29
30
+ var (
31
+ errLameReferral = errors .New ("lame referral" )
32
+ errCannotUnmarshalDNSMessage = errors .New ("cannot unmarshal DNS message" )
33
+ errCannotMarshalDNSMessage = errors .New ("cannot marshal DNS message" )
34
+ errServerMisbehaving = errors .New ("server misbehaving" )
35
+ errInvalidDNSResponse = errors .New ("invalid DNS response" )
36
+ errNoAnswerFromDNSServer = errors .New ("no answer from DNS server" )
37
+
38
+ // errServerTemporarlyMisbehaving is like errServerMisbehaving, except
39
+ // that when it gets translated to a DNSError, the IsTemporary field
40
+ // gets set to true.
41
+ errServerTemporarlyMisbehaving = errors .New ("server misbehaving" )
42
+ )
43
+
30
44
func newRequest (q dnsmessage.Question ) (id uint16 , udpReq , tcpReq []byte , err error ) {
31
45
id = uint16 (rand .Int ()) ^ uint16 (time .Now ().UnixNano ())
32
46
b := dnsmessage .NewBuilder (make ([]byte , 2 , 514 ), dnsmessage.Header {ID : id , RecursionDesired : true })
@@ -105,14 +119,14 @@ func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte)
105
119
var p dnsmessage.Parser
106
120
h , err := p .Start (b [:n ])
107
121
if err != nil {
108
- return dnsmessage.Parser {}, dnsmessage.Header {}, errors . New ( "cannot unmarshal DNS message" )
122
+ return dnsmessage.Parser {}, dnsmessage.Header {}, errCannotUnmarshalDNSMessage
109
123
}
110
124
q , err := p .Question ()
111
125
if err != nil {
112
- return dnsmessage.Parser {}, dnsmessage.Header {}, errors . New ( "cannot unmarshal DNS message" )
126
+ return dnsmessage.Parser {}, dnsmessage.Header {}, errCannotUnmarshalDNSMessage
113
127
}
114
128
if ! checkResponse (id , query , h , q ) {
115
- return dnsmessage.Parser {}, dnsmessage.Header {}, errors . New ( "invalid DNS response" )
129
+ return dnsmessage.Parser {}, dnsmessage.Header {}, errInvalidDNSResponse
116
130
}
117
131
return p , h , nil
118
132
}
@@ -122,7 +136,7 @@ func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Que
122
136
q .Class = dnsmessage .ClassINET
123
137
id , udpReq , tcpReq , err := newRequest (q )
124
138
if err != nil {
125
- return dnsmessage.Parser {}, dnsmessage.Header {}, errors . New ( "cannot marshal DNS message" )
139
+ return dnsmessage.Parser {}, dnsmessage.Header {}, errCannotMarshalDNSMessage
126
140
}
127
141
for _ , network := range []string {"udp" , "tcp" } {
128
142
ctx , cancel := context .WithDeadline (ctx , time .Now ().Add (timeout ))
@@ -147,31 +161,31 @@ func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Que
147
161
return dnsmessage.Parser {}, dnsmessage.Header {}, mapErr (err )
148
162
}
149
163
if err := p .SkipQuestion (); err != dnsmessage .ErrSectionDone {
150
- return dnsmessage.Parser {}, dnsmessage.Header {}, errors . New ( "invalid DNS response" )
164
+ return dnsmessage.Parser {}, dnsmessage.Header {}, errInvalidDNSResponse
151
165
}
152
166
if h .Truncated { // see RFC 5966
153
167
continue
154
168
}
155
169
return p , h , nil
156
170
}
157
- return dnsmessage.Parser {}, dnsmessage.Header {}, errors . New ( "no answer from DNS server" )
171
+ return dnsmessage.Parser {}, dnsmessage.Header {}, errNoAnswerFromDNSServer
158
172
}
159
173
160
174
// checkHeader performs basic sanity checks on the header.
161
175
func checkHeader (p * dnsmessage.Parser , h dnsmessage.Header , name , server string ) error {
176
+ if h .RCode == dnsmessage .RCodeNameError {
177
+ return errNoSuchHost
178
+ }
179
+
162
180
_ , err := p .AnswerHeader ()
163
181
if err != nil && err != dnsmessage .ErrSectionDone {
164
- return & DNSError {
165
- Err : "cannot unmarshal DNS message" ,
166
- Name : name ,
167
- Server : server ,
168
- }
182
+ return errCannotUnmarshalDNSMessage
169
183
}
170
184
171
185
// libresolv continues to the next server when it receives
172
186
// an invalid referral response. See golang.org/issue/15434.
173
187
if h .RCode == dnsmessage .RCodeSuccess && ! h .Authoritative && ! h .RecursionAvailable && err == dnsmessage .ErrSectionDone {
174
- return & DNSError { Err : "lame referral" , Name : name , Server : server }
188
+ return errLameReferral
175
189
}
176
190
177
191
if h .RCode != dnsmessage .RCodeSuccess && h .RCode != dnsmessage .RCodeNameError {
@@ -180,11 +194,10 @@ func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header, name, server string)
180
194
// a name error and we didn't get success,
181
195
// the server is behaving incorrectly or
182
196
// having temporary trouble.
183
- err := & DNSError {Err : "server misbehaving" , Name : name , Server : server }
184
197
if h .RCode == dnsmessage .RCodeServerFailure {
185
- err . IsTemporary = true
198
+ return errServerTemporarlyMisbehaving
186
199
}
187
- return err
200
+ return errServerMisbehaving
188
201
}
189
202
190
203
return nil
@@ -194,28 +207,16 @@ func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type, name, server stri
194
207
for {
195
208
h , err := p .AnswerHeader ()
196
209
if err == dnsmessage .ErrSectionDone {
197
- return & DNSError {
198
- Err : errNoSuchHost .Error (),
199
- Name : name ,
200
- Server : server ,
201
- }
210
+ return errNoSuchHost
202
211
}
203
212
if err != nil {
204
- return & DNSError {
205
- Err : "cannot unmarshal DNS message" ,
206
- Name : name ,
207
- Server : server ,
208
- }
213
+ return errCannotUnmarshalDNSMessage
209
214
}
210
215
if h .Type == qtype {
211
216
return nil
212
217
}
213
218
if err := p .SkipAnswer (); err != nil {
214
- return & DNSError {
215
- Err : "cannot unmarshal DNS message" ,
216
- Name : name ,
217
- Server : server ,
218
- }
219
+ return errCannotUnmarshalDNSMessage
219
220
}
220
221
}
221
222
}
@@ -229,7 +230,7 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string,
229
230
230
231
n , err := dnsmessage .NewName (name )
231
232
if err != nil {
232
- return dnsmessage.Parser {}, "" , errors . New ( "cannot marshal DNS message" )
233
+ return dnsmessage.Parser {}, "" , errCannotMarshalDNSMessage
233
234
}
234
235
q := dnsmessage.Question {
235
236
Name : n ,
@@ -243,38 +244,62 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string,
243
244
244
245
p , h , err := r .exchange (ctx , server , q , cfg .timeout )
245
246
if err != nil {
246
- lastErr = & DNSError {
247
+ dnsErr : = & DNSError {
247
248
Err : err .Error (),
248
249
Name : name ,
249
250
Server : server ,
250
251
}
251
252
if nerr , ok := err .(Error ); ok && nerr .Timeout () {
252
- lastErr .( * DNSError ) .IsTimeout = true
253
+ dnsErr .IsTimeout = true
253
254
}
254
255
// Set IsTemporary for socket-level errors. Note that this flag
255
256
// may also be used to indicate a SERVFAIL response.
256
257
if _ , ok := err .(* OpError ); ok {
257
- lastErr .( * DNSError ) .IsTemporary = true
258
+ dnsErr .IsTemporary = true
258
259
}
260
+ lastErr = dnsErr
259
261
continue
260
262
}
261
263
262
- // The name does not exist, so trying another server won't help.
263
- //
264
- // TODO: indicate this in a more obvious way, such as a field on DNSError?
265
- if h .RCode == dnsmessage .RCodeNameError {
266
- return dnsmessage.Parser {}, "" , & DNSError {Err : errNoSuchHost .Error (), Name : name , Server : server }
267
- }
268
-
269
- lastErr = checkHeader (& p , h , name , server )
270
- if lastErr != nil {
264
+ if err := checkHeader (& p , h , name , server ); err != nil {
265
+ dnsErr := & DNSError {
266
+ Err : err .Error (),
267
+ Name : name ,
268
+ Server : server ,
269
+ }
270
+ if err == errServerTemporarlyMisbehaving {
271
+ dnsErr .IsTemporary = true
272
+ }
273
+ if err == errNoSuchHost {
274
+ // The name does not exist, so trying
275
+ // another server won't help.
276
+ //
277
+ // TODO: indicate this in a more
278
+ // obvious way, such as a field on
279
+ // DNSError?
280
+ return p , server , dnsErr
281
+ }
282
+ lastErr = dnsErr
271
283
continue
272
284
}
273
285
274
- lastErr = skipToAnswer (& p , qtype , name , server )
275
- if lastErr == nil {
286
+ err = skipToAnswer (& p , qtype , name , server )
287
+ if err == nil {
276
288
return p , server , nil
277
289
}
290
+ lastErr = & DNSError {
291
+ Err : err .Error (),
292
+ Name : name ,
293
+ Server : server ,
294
+ }
295
+ if err == errNoSuchHost {
296
+ // The name does not exist, so trying another
297
+ // server won't help.
298
+ //
299
+ // TODO: indicate this in a more obvious way,
300
+ // such as a field on DNSError?
301
+ return p , server , lastErr
302
+ }
278
303
}
279
304
}
280
305
return dnsmessage.Parser {}, "" , lastErr
0 commit comments