Contents of /trunk/glibc/patches/glibc-2.22-CVE-2015-7547.patch
Parent Directory | Revision Log
Revision 2763 -
(show annotations)
(download)
Wed Feb 17 08:39:35 2016 UTC (8 years, 7 months ago) by niro
File size: 23860 byte(s)
Wed Feb 17 08:39:35 2016 UTC (8 years, 7 months ago) by niro
File size: 23860 byte(s)
-fixed CVE-2015-7547, see https://sourceware.org/ml/libc-alpha/2016-02/msg00416.html
1 | CVE-2015-7547 |
2 | |
3 | 2016-02-15 Carlos O'Donell <carlos@redhat.com> |
4 | |
5 | [BZ #18665] |
6 | * resolv/nss_dns/dns-host.c (gaih_getanswer_slice): Always set |
7 | *herrno_p. |
8 | (gaih_getanswer): Document functional behviour. Return tryagain |
9 | if any result is tryagain. |
10 | * resolv/res_query.c (__libc_res_nsearch): Set buffer size to zero |
11 | when freed. |
12 | * resolv/res_send.c: Add copyright text. |
13 | (__libc_res_nsend): Document that MAXPACKET is expected. |
14 | (send_vc): Document. Remove buffer reuse. |
15 | (send_dg): Document. Remove buffer reuse. Set *thisanssizp to set the |
16 | size of the buffer. Add Dprint for truncated UDP buffer. |
17 | |
18 | diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c |
19 | index a255d5e..47cfe27 100644 |
20 | --- a/resolv/nss_dns/dns-host.c |
21 | +++ b/resolv/nss_dns/dns-host.c |
22 | @@ -1031,7 +1031,10 @@ gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, |
23 | int h_namelen = 0; |
24 | |
25 | if (ancount == 0) |
26 | - return NSS_STATUS_NOTFOUND; |
27 | + { |
28 | + *h_errnop = HOST_NOT_FOUND; |
29 | + return NSS_STATUS_NOTFOUND; |
30 | + } |
31 | |
32 | while (ancount-- > 0 && cp < end_of_message && had_error == 0) |
33 | { |
34 | @@ -1208,7 +1211,14 @@ gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, |
35 | /* Special case here: if the resolver sent a result but it only |
36 | contains a CNAME while we are looking for a T_A or T_AAAA record, |
37 | we fail with NOTFOUND instead of TRYAGAIN. */ |
38 | - return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; |
39 | + if (canon != NULL) |
40 | + { |
41 | + *h_errnop = HOST_NOT_FOUND; |
42 | + return NSS_STATUS_NOTFOUND; |
43 | + } |
44 | + |
45 | + *h_errnop = NETDB_INTERNAL; |
46 | + return NSS_STATUS_TRYAGAIN; |
47 | } |
48 | |
49 | |
50 | @@ -1222,11 +1232,101 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2, |
51 | |
52 | enum nss_status status = NSS_STATUS_NOTFOUND; |
53 | |
54 | + /* Combining the NSS status of two distinct queries requires some |
55 | + compromise and attention to symmetry (A or AAAA queries can be |
56 | + returned in any order). What follows is a breakdown of how this |
57 | + code is expected to work and why. We discuss only SUCCESS, |
58 | + TRYAGAIN, NOTFOUND and UNAVAIL, since they are the only returns |
59 | + that apply (though RETURN and MERGE exist). We make a distinction |
60 | + between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable). |
61 | + A recoverable TRYAGAIN is almost always due to buffer size issues |
62 | + and returns ERANGE in errno and the caller is expected to retry |
63 | + with a larger buffer. |
64 | + |
65 | + Lastly, you may be tempted to make significant changes to the |
66 | + conditions in this code to bring about symmetry between responses. |
67 | + Please don't change anything without due consideration for |
68 | + expected application behaviour. Some of the synthesized responses |
69 | + aren't very well thought out and sometimes appear to imply that |
70 | + IPv4 responses are always answer 1, and IPv6 responses are always |
71 | + answer 2, but that's not true (see the implemetnation of send_dg |
72 | + and send_vc to see response can arrive in any order, particlarly |
73 | + for UDP). However, we expect it holds roughly enough of the time |
74 | + that this code works, but certainly needs to be fixed to make this |
75 | + a more robust implementation. |
76 | + |
77 | + ---------------------------------------------- |
78 | + | Answer 1 Status / | Synthesized | Reason | |
79 | + | Answer 2 Status | Status | | |
80 | + |--------------------------------------------| |
81 | + | SUCCESS/SUCCESS | SUCCESS | [1] | |
82 | + | SUCCESS/TRYAGAIN | TRYAGAIN | [5] | |
83 | + | SUCCESS/TRYAGAIN' | SUCCESS | [1] | |
84 | + | SUCCESS/NOTFOUND | SUCCESS | [1] | |
85 | + | SUCCESS/UNAVAIL | SUCCESS | [1] | |
86 | + | TRYAGAIN/SUCCESS | TRYAGAIN | [2] | |
87 | + | TRYAGAIN/TRYAGAIN | TRYAGAIN | [2] | |
88 | + | TRYAGAIN/TRYAGAIN' | TRYAGAIN | [2] | |
89 | + | TRYAGAIN/NOTFOUND | TRYAGAIN | [2] | |
90 | + | TRYAGAIN/UNAVAIL | TRYAGAIN | [2] | |
91 | + | TRYAGAIN'/SUCCESS | SUCCESS | [3] | |
92 | + | TRYAGAIN'/TRYAGAIN | TRYAGAIN | [3] | |
93 | + | TRYAGAIN'/TRYAGAIN' | TRYAGAIN' | [3] | |
94 | + | TRYAGAIN'/NOTFOUND | TRYAGAIN' | [3] | |
95 | + | TRYAGAIN'/UNAVAIL | UNAVAIL | [3] | |
96 | + | NOTFOUND/SUCCESS | SUCCESS | [3] | |
97 | + | NOTFOUND/TRYAGAIN | TRYAGAIN | [3] | |
98 | + | NOTFOUND/TRYAGAIN' | TRYAGAIN' | [3] | |
99 | + | NOTFOUND/NOTFOUND | NOTFOUND | [3] | |
100 | + | NOTFOUND/UNAVAIL | UNAVAIL | [3] | |
101 | + | UNAVAIL/SUCCESS | UNAVAIL | [4] | |
102 | + | UNAVAIL/TRYAGAIN | UNAVAIL | [4] | |
103 | + | UNAVAIL/TRYAGAIN' | UNAVAIL | [4] | |
104 | + | UNAVAIL/NOTFOUND | UNAVAIL | [4] | |
105 | + | UNAVAIL/UNAVAIL | UNAVAIL | [4] | |
106 | + ---------------------------------------------- |
107 | + |
108 | + [1] If the first response is a success we return success. |
109 | + This ignores the state of the second answer and in fact |
110 | + incorrectly sets errno and h_errno to that of the second |
111 | + answer. However because the response is a success we ignore |
112 | + *errnop and *h_errnop (though that means you touched errno on |
113 | + success). We are being conservative here and returning the |
114 | + likely IPv4 response in the first answer as a success. |
115 | + |
116 | + [2] If the first response is a recoverable TRYAGAIN we return |
117 | + that instead of looking at the second response. The |
118 | + expectation here is that we have failed to get an IPv4 response |
119 | + and should retry both queries. |
120 | + |
121 | + [3] If the first response was not a SUCCESS and the second |
122 | + response is not NOTFOUND (had a SUCCESS, need to TRYAGAIN, |
123 | + or failed entirely e.g. TRYAGAIN' and UNAVAIL) then use the |
124 | + result from the second response, otherwise the first responses |
125 | + status is used. Again we have some odd side-effects when the |
126 | + second response is NOTFOUND because we overwrite *errnop and |
127 | + *h_errnop that means that a first answer of NOTFOUND might see |
128 | + its *errnop and *h_errnop values altered. Whether it matters |
129 | + in practice that a first response NOTFOUND has the wrong |
130 | + *errnop and *h_errnop is undecided. |
131 | + |
132 | + [4] If the first response is UNAVAIL we return that instead of |
133 | + looking at the second response. The expectation here is that |
134 | + it will have failed similarly e.g. configuration failure. |
135 | + |
136 | + [5] Testing this code is complicated by the fact that truncated |
137 | + second response buffers might be returned as SUCCESS if the |
138 | + first answer is a SUCCESS. To fix this we add symmetry to |
139 | + TRYAGAIN with the second response. If the second response |
140 | + is a recoverable error we now return TRYAGIN even if the first |
141 | + response was SUCCESS. */ |
142 | + |
143 | if (anslen1 > 0) |
144 | status = gaih_getanswer_slice(answer1, anslen1, qname, |
145 | &pat, &buffer, &buflen, |
146 | errnop, h_errnop, ttlp, |
147 | &first); |
148 | + |
149 | if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND |
150 | || (status == NSS_STATUS_TRYAGAIN |
151 | /* We want to look at the second answer in case of an |
152 | @@ -1242,8 +1342,15 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2, |
153 | &pat, &buffer, &buflen, |
154 | errnop, h_errnop, ttlp, |
155 | &first); |
156 | + /* Use the second response status in some cases. */ |
157 | if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND) |
158 | status = status2; |
159 | + /* Do not return a truncated second response (unless it was |
160 | + unavoidable e.g. unrecoverable TRYAGAIN). */ |
161 | + if (status == NSS_STATUS_SUCCESS |
162 | + && (status2 == NSS_STATUS_TRYAGAIN |
163 | + && *errnop == ERANGE && *h_errnop != NO_RECOVERY)) |
164 | + status = NSS_STATUS_TRYAGAIN; |
165 | } |
166 | |
167 | return status; |
168 | diff --git a/resolv/res_query.c b/resolv/res_query.c |
169 | index 4a9b3b3..95470a9 100644 |
170 | --- a/resolv/res_query.c |
171 | +++ b/resolv/res_query.c |
172 | @@ -396,6 +396,7 @@ __libc_res_nsearch(res_state statp, |
173 | { |
174 | free (*answerp2); |
175 | *answerp2 = NULL; |
176 | + *nanswerp2 = 0; |
177 | *answerp2_malloced = 0; |
178 | } |
179 | } |
180 | @@ -447,6 +448,7 @@ __libc_res_nsearch(res_state statp, |
181 | { |
182 | free (*answerp2); |
183 | *answerp2 = NULL; |
184 | + *nanswerp2 = 0; |
185 | *answerp2_malloced = 0; |
186 | } |
187 | |
188 | @@ -521,6 +523,7 @@ __libc_res_nsearch(res_state statp, |
189 | { |
190 | free (*answerp2); |
191 | *answerp2 = NULL; |
192 | + *nanswerp2 = 0; |
193 | *answerp2_malloced = 0; |
194 | } |
195 | if (saved_herrno != -1) |
196 | diff --git a/resolv/res_send.c b/resolv/res_send.c |
197 | index a968b95..21843f1 100644 |
198 | --- a/resolv/res_send.c |
199 | +++ b/resolv/res_send.c |
200 | @@ -1,3 +1,20 @@ |
201 | +/* Copyright (C) 2016 Free Software Foundation, Inc. |
202 | + This file is part of the GNU C Library. |
203 | + |
204 | + The GNU C Library is free software; you can redistribute it and/or |
205 | + modify it under the terms of the GNU Lesser General Public |
206 | + License as published by the Free Software Foundation; either |
207 | + version 2.1 of the License, or (at your option) any later version. |
208 | + |
209 | + The GNU C Library is distributed in the hope that it will be useful, |
210 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
211 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
212 | + Lesser General Public License for more details. |
213 | + |
214 | + You should have received a copy of the GNU Lesser General Public |
215 | + License along with the GNU C Library; if not, see |
216 | + <http://www.gnu.org/licenses/>. */ |
217 | + |
218 | /* |
219 | * Copyright (c) 1985, 1989, 1993 |
220 | * The Regents of the University of California. All rights reserved. |
221 | @@ -355,6 +372,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, |
222 | #ifdef USE_HOOKS |
223 | if (__glibc_unlikely (statp->qhook || statp->rhook)) { |
224 | if (anssiz < MAXPACKET && ansp) { |
225 | + /* Always allocate MAXPACKET, callers expect |
226 | + this specific size. */ |
227 | u_char *buf = malloc (MAXPACKET); |
228 | if (buf == NULL) |
229 | return (-1); |
230 | @@ -630,6 +649,77 @@ get_nsaddr (res_state statp, int n) |
231 | return (struct sockaddr *) (void *) &statp->nsaddr_list[n]; |
232 | } |
233 | |
234 | +/* The send_vc function is responsible for sending a DNS query over TCP |
235 | + to the nameserver numbered NS from the res_state STATP i.e. |
236 | + EXT(statp).nssocks[ns]. The function supports sending both IPv4 and |
237 | + IPv6 queries at the same serially on the same socket. |
238 | + |
239 | + Please note that for TCP there is no way to disable sending both |
240 | + queries, unlike UDP, which honours RES_SNGLKUP and RES_SNGLKUPREOP |
241 | + and sends the queries serially and waits for the result after each |
242 | + sent query. This implemetnation should be corrected to honour these |
243 | + options. |
244 | + |
245 | + Please also note that for TCP we send both queries over the same |
246 | + socket one after another. This technically violates best practice |
247 | + since the server is allowed to read the first query, respond, and |
248 | + then close the socket (to service another client). If the server |
249 | + does this, then the remaining second query in the socket data buffer |
250 | + will cause the server to send the client an RST which will arrive |
251 | + asynchronously and the client's OS will likely tear down the socket |
252 | + receive buffer resulting in a potentially short read and lost |
253 | + response data. This will force the client to retry the query again, |
254 | + and this process may repeat until all servers and connection resets |
255 | + are exhausted and then the query will fail. It's not known if this |
256 | + happens with any frequency in real DNS server implementations. This |
257 | + implementation should be corrected to use two sockets by default for |
258 | + parallel queries. |
259 | + |
260 | + The query stored in BUF of BUFLEN length is sent first followed by |
261 | + the query stored in BUF2 of BUFLEN2 length. Queries are sent |
262 | + serially on the same socket. |
263 | + |
264 | + Answers to the query are stored firstly in *ANSP up to a max of |
265 | + *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP |
266 | + is non-NULL (to indicate that modifying the answer buffer is allowed) |
267 | + then malloc is used to allocate a new response buffer and ANSCP and |
268 | + ANSP will both point to the new buffer. If more than *ANSSIZP bytes |
269 | + are needed but ANSCP is NULL, then as much of the response as |
270 | + possible is read into the buffer, but the results will be truncated. |
271 | + When truncation happens because of a small answer buffer the DNS |
272 | + packets header feild TC will bet set to 1, indicating a truncated |
273 | + message and the rest of the socket data will be read and discarded. |
274 | + |
275 | + Answers to the query are stored secondly in *ANSP2 up to a max of |
276 | + *ANSSIZP2 bytes, with the actual response length stored in |
277 | + *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2 |
278 | + is non-NULL (required for a second query) then malloc is used to |
279 | + allocate a new response buffer, *ANSSIZP2 is set to the new buffer |
280 | + size and *ANSP2_MALLOCED is set to 1. |
281 | + |
282 | + The ANSP2_MALLOCED argument will eventually be removed as the |
283 | + change in buffer pointer can be used to detect the buffer has |
284 | + changed and that the caller should use free on the new buffer. |
285 | + |
286 | + Note that the answers may arrive in any order from the server and |
287 | + therefore the first and second answer buffers may not correspond to |
288 | + the first and second queries. |
289 | + |
290 | + It is not supported to call this function with a non-NULL ANSP2 |
291 | + but a NULL ANSCP. Put another way, you can call send_vc with a |
292 | + single unmodifiable buffer or two modifiable buffers, but no other |
293 | + combination is supported. |
294 | + |
295 | + It is the caller's responsibility to free the malloc allocated |
296 | + buffers by detecting that the pointers have changed from their |
297 | + original values i.e. *ANSCP or *ANSP2 has changed. |
298 | + |
299 | + If errors are encountered then *TERRNO is set to an appropriate |
300 | + errno value and a zero result is returned for a recoverable error, |
301 | + and a less-than zero result is returned for a non-recoverable error. |
302 | + |
303 | + If no errors are encountered then *TERRNO is left unmodified and |
304 | + a the length of the first response in bytes is returned. */ |
305 | static int |
306 | send_vc(res_state statp, |
307 | const u_char *buf, int buflen, const u_char *buf2, int buflen2, |
308 | @@ -639,11 +729,7 @@ send_vc(res_state statp, |
309 | { |
310 | const HEADER *hp = (HEADER *) buf; |
311 | const HEADER *hp2 = (HEADER *) buf2; |
312 | - u_char *ans = *ansp; |
313 | - int orig_anssizp = *anssizp; |
314 | - // XXX REMOVE |
315 | - // int anssiz = *anssizp; |
316 | - HEADER *anhp = (HEADER *) ans; |
317 | + HEADER *anhp = (HEADER *) *ansp; |
318 | struct sockaddr *nsap = get_nsaddr (statp, ns); |
319 | int truncating, connreset, n; |
320 | /* On some architectures compiler might emit a warning indicating |
321 | @@ -731,6 +817,8 @@ send_vc(res_state statp, |
322 | * Receive length & response |
323 | */ |
324 | int recvresp1 = 0; |
325 | + /* Skip the second response if there is no second query. |
326 | + To do that we mark the second response as received. */ |
327 | int recvresp2 = buf2 == NULL; |
328 | uint16_t rlen16; |
329 | read_len: |
330 | @@ -767,40 +855,14 @@ send_vc(res_state statp, |
331 | u_char **thisansp; |
332 | int *thisresplenp; |
333 | if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) { |
334 | + /* We have not received any responses |
335 | + yet or we only have one response to |
336 | + receive. */ |
337 | thisanssizp = anssizp; |
338 | thisansp = anscp ?: ansp; |
339 | assert (anscp != NULL || ansp2 == NULL); |
340 | thisresplenp = &resplen; |
341 | } else { |
342 | - if (*anssizp != MAXPACKET) { |
343 | - /* No buffer allocated for the first |
344 | - reply. We can try to use the rest |
345 | - of the user-provided buffer. */ |
346 | -#if __GNUC_PREREQ (4, 7) |
347 | - DIAG_PUSH_NEEDS_COMMENT; |
348 | - DIAG_IGNORE_NEEDS_COMMENT (5, "-Wmaybe-uninitialized"); |
349 | -#endif |
350 | -#if _STRING_ARCH_unaligned |
351 | - *anssizp2 = orig_anssizp - resplen; |
352 | - *ansp2 = *ansp + resplen; |
353 | -#else |
354 | - int aligned_resplen |
355 | - = ((resplen + __alignof__ (HEADER) - 1) |
356 | - & ~(__alignof__ (HEADER) - 1)); |
357 | - *anssizp2 = orig_anssizp - aligned_resplen; |
358 | - *ansp2 = *ansp + aligned_resplen; |
359 | -#endif |
360 | -#if __GNUC_PREREQ (4, 7) |
361 | - DIAG_POP_NEEDS_COMMENT; |
362 | -#endif |
363 | - } else { |
364 | - /* The first reply did not fit into the |
365 | - user-provided buffer. Maybe the second |
366 | - answer will. */ |
367 | - *anssizp2 = orig_anssizp; |
368 | - *ansp2 = *ansp; |
369 | - } |
370 | - |
371 | thisanssizp = anssizp2; |
372 | thisansp = ansp2; |
373 | thisresplenp = resplen2; |
374 | @@ -804,10 +870,14 @@ send_vc(res_state statp, |
375 | anhp = (HEADER *) *thisansp; |
376 | |
377 | *thisresplenp = rlen; |
378 | - if (rlen > *thisanssizp) { |
379 | - /* Yes, we test ANSCP here. If we have two buffers |
380 | - both will be allocatable. */ |
381 | - if (__glibc_likely (anscp != NULL)) { |
382 | + /* Is the answer buffer too small? */ |
383 | + if (*thisanssizp < rlen) { |
384 | + /* If the current buffer is not the the static |
385 | + user-supplied buffer then we can reallocate |
386 | + it. */ |
387 | + if (thisansp != NULL && thisansp != ansp) { |
388 | + /* Always allocate MAXPACKET, callers expect |
389 | + this specific size. */ |
390 | u_char *newp = malloc (MAXPACKET); |
391 | if (newp == NULL) { |
392 | *terrno = ENOMEM; |
393 | @@ -819,6 +889,9 @@ send_vc(res_state statp, |
394 | if (thisansp == ansp2) |
395 | *ansp2_malloced = 1; |
396 | anhp = (HEADER *) newp; |
397 | + /* A uint16_t can't be larger than MAXPACKET |
398 | + thus it's safe to allocate MAXPACKET but |
399 | + read RLEN bytes instead. */ |
400 | len = rlen; |
401 | } else { |
402 | Dprint(statp->options & RES_DEBUG, |
403 | @@ -948,6 +1021,66 @@ reopen (res_state statp, int *terrno, int ns) |
404 | return 1; |
405 | } |
406 | |
407 | +/* The send_dg function is responsible for sending a DNS query over UDP |
408 | + to the nameserver numbered NS from the res_state STATP i.e. |
409 | + EXT(statp).nssocks[ns]. The function supports IPv4 and IPv6 queries |
410 | + along with the ability to send the query in parallel for both stacks |
411 | + (default) or serially (RES_SINGLKUP). It also supports serial lookup |
412 | + with a close and reopen of the socket used to talk to the server |
413 | + (RES_SNGLKUPREOP) to work around broken name servers. |
414 | + |
415 | + The query stored in BUF of BUFLEN length is sent first followed by |
416 | + the query stored in BUF2 of BUFLEN2 length. Queries are sent |
417 | + in parallel (default) or serially (RES_SINGLKUP or RES_SNGLKUPREOP). |
418 | + |
419 | + Answers to the query are stored firstly in *ANSP up to a max of |
420 | + *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP |
421 | + is non-NULL (to indicate that modifying the answer buffer is allowed) |
422 | + then malloc is used to allocate a new response buffer and ANSCP and |
423 | + ANSP will both point to the new buffer. If more than *ANSSIZP bytes |
424 | + are needed but ANSCP is NULL, then as much of the response as |
425 | + possible is read into the buffer, but the results will be truncated. |
426 | + When truncation happens because of a small answer buffer the DNS |
427 | + packets header feild TC will bet set to 1, indicating a truncated |
428 | + message, while the rest of the UDP packet is discarded. |
429 | + |
430 | + Answers to the query are stored secondly in *ANSP2 up to a max of |
431 | + *ANSSIZP2 bytes, with the actual response length stored in |
432 | + *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2 |
433 | + is non-NULL (required for a second query) then malloc is used to |
434 | + allocate a new response buffer, *ANSSIZP2 is set to the new buffer |
435 | + size and *ANSP2_MALLOCED is set to 1. |
436 | + |
437 | + The ANSP2_MALLOCED argument will eventually be removed as the |
438 | + change in buffer pointer can be used to detect the buffer has |
439 | + changed and that the caller should use free on the new buffer. |
440 | + |
441 | + Note that the answers may arrive in any order from the server and |
442 | + therefore the first and second answer buffers may not correspond to |
443 | + the first and second queries. |
444 | + |
445 | + It is not supported to call this function with a non-NULL ANSP2 |
446 | + but a NULL ANSCP. Put another way, you can call send_vc with a |
447 | + single unmodifiable buffer or two modifiable buffers, but no other |
448 | + combination is supported. |
449 | + |
450 | + It is the caller's responsibility to free the malloc allocated |
451 | + buffers by detecting that the pointers have changed from their |
452 | + original values i.e. *ANSCP or *ANSP2 has changed. |
453 | + |
454 | + If an answer is truncated because of UDP datagram DNS limits then |
455 | + *V_CIRCUIT is set to 1 and the return value non-zero to indicate to |
456 | + the caller to retry with TCP. The value *GOTSOMEWHERE is set to 1 |
457 | + if any progress was made reading a response from the nameserver and |
458 | + is used by the caller to distinguish between ECONNREFUSED and |
459 | + ETIMEDOUT (the latter if *GOTSOMEWHERE is 1). |
460 | + |
461 | + If errors are encountered then *TERRNO is set to an appropriate |
462 | + errno value and a zero result is returned for a recoverable error, |
463 | + and a less-than zero result is returned for a non-recoverable error. |
464 | + |
465 | + If no errors are encountered then *TERRNO is left unmodified and |
466 | + a the length of the first response in bytes is returned. */ |
467 | static int |
468 | send_dg(res_state statp, |
469 | const u_char *buf, int buflen, const u_char *buf2, int buflen2, |
470 | @@ -957,8 +1090,6 @@ send_dg(res_state statp, |
471 | { |
472 | const HEADER *hp = (HEADER *) buf; |
473 | const HEADER *hp2 = (HEADER *) buf2; |
474 | - u_char *ans = *ansp; |
475 | - int orig_anssizp = *anssizp; |
476 | struct timespec now, timeout, finish; |
477 | struct pollfd pfd[1]; |
478 | int ptimeout; |
479 | @@ -991,6 +1122,8 @@ send_dg(res_state statp, |
480 | int need_recompute = 0; |
481 | int nwritten = 0; |
482 | int recvresp1 = 0; |
483 | + /* Skip the second response if there is no second query. |
484 | + To do that we mark the second response as received. */ |
485 | int recvresp2 = buf2 == NULL; |
486 | pfd[0].fd = EXT(statp).nssocks[ns]; |
487 | pfd[0].events = POLLOUT; |
488 | @@ -1154,55 +1287,56 @@ send_dg(res_state statp, |
489 | int *thisresplenp; |
490 | |
491 | if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) { |
492 | + /* We have not received any responses |
493 | + yet or we only have one response to |
494 | + receive. */ |
495 | thisanssizp = anssizp; |
496 | thisansp = anscp ?: ansp; |
497 | assert (anscp != NULL || ansp2 == NULL); |
498 | thisresplenp = &resplen; |
499 | } else { |
500 | - if (*anssizp != MAXPACKET) { |
501 | - /* No buffer allocated for the first |
502 | - reply. We can try to use the rest |
503 | - of the user-provided buffer. */ |
504 | -#if _STRING_ARCH_unaligned |
505 | - *anssizp2 = orig_anssizp - resplen; |
506 | - *ansp2 = *ansp + resplen; |
507 | -#else |
508 | - int aligned_resplen |
509 | - = ((resplen + __alignof__ (HEADER) - 1) |
510 | - & ~(__alignof__ (HEADER) - 1)); |
511 | - *anssizp2 = orig_anssizp - aligned_resplen; |
512 | - *ansp2 = *ansp + aligned_resplen; |
513 | -#endif |
514 | - } else { |
515 | - /* The first reply did not fit into the |
516 | - user-provided buffer. Maybe the second |
517 | - answer will. */ |
518 | - *anssizp2 = orig_anssizp; |
519 | - *ansp2 = *ansp; |
520 | - } |
521 | - |
522 | thisanssizp = anssizp2; |
523 | thisansp = ansp2; |
524 | thisresplenp = resplen2; |
525 | } |
526 | |
527 | if (*thisanssizp < MAXPACKET |
528 | - /* Yes, we test ANSCP here. If we have two buffers |
529 | - both will be allocatable. */ |
530 | - && anscp |
531 | + /* If the current buffer is not the the static |
532 | + user-supplied buffer then we can reallocate |
533 | + it. */ |
534 | + && (thisansp != NULL && thisansp != ansp) |
535 | #ifdef FIONREAD |
536 | + /* Is the size too small? */ |
537 | && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0 |
538 | || *thisanssizp < *thisresplenp) |
539 | #endif |
540 | ) { |
541 | + /* Always allocate MAXPACKET, callers expect |
542 | + this specific size. */ |
543 | u_char *newp = malloc (MAXPACKET); |
544 | if (newp != NULL) { |
545 | - *anssizp = MAXPACKET; |
546 | - *thisansp = ans = newp; |
547 | + *thisanssizp = MAXPACKET; |
548 | + *thisansp = newp; |
549 | if (thisansp == ansp2) |
550 | *ansp2_malloced = 1; |
551 | } |
552 | } |
553 | + /* We could end up with truncation if anscp was NULL |
554 | + (not allowed to change caller's buffer) and the |
555 | + response buffer size is too small. This isn't a |
556 | + reliable way to detect truncation because the ioctl |
557 | + may be an inaccurate report of the UDP message size. |
558 | + Therefore we use this only to issue debug output. |
559 | + To do truncation accurately with UDP we need |
560 | + MSG_TRUNC which is only available on Linux. We |
561 | + can abstract out the Linux-specific feature in the |
562 | + future to detect truncation. */ |
563 | + if (__glibc_unlikely (*thisanssizp < *thisresplenp)) { |
564 | + Dprint(statp->options & RES_DEBUG, |
565 | + (stdout, ";; response may be truncated (UDP)\n") |
566 | + ); |
567 | + } |
568 | + |
569 | HEADER *anhp = (HEADER *) *thisansp; |
570 | socklen_t fromlen = sizeof(struct sockaddr_in6); |
571 | assert (sizeof(from) <= fromlen); |
572 |