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