aboutsummaryrefslogtreecommitdiffstats
path: root/main/libc0.9.32/0004-resolv-fix-resolver-to-return-TRY_AGAIN-on-timeout.patch
blob: 2978ee806ade08349b2647f880edf934f659a180 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
From d7d9bf8f32235bfa781d632a0a3ba6b544062ce1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
Date: Thu, 7 Jul 2011 18:47:26 +0300
Subject: [PATCH] resolv: fix resolver to return TRY_AGAIN on timeout
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes the internal __dns_lookup to get a h_errno pointer so
it works nicely with the _r variants. Additionally the function is
modified to permanent error if the static buffer lengths are not
enough. And finally it fixed to return TRY_AGAIN if the nameservers
timeout.

res_search is fixed to continue searching if we receive TRY_AGAIN.
It could be a problem with the specific search domain's server
and not necessarily a problem in the recursive resolver we are
querying. For same reason, it does not make sense to differentiate
timeout or SERVFAIL error reply.

The biggest issue this fixes is that we now properly set h_errno
to TRY_AGAIN if upstream nameserver(s) timed out. Previously we
would have returned NETDB_INTERNAL.

Signed-off-by: Timo Teräs <timo.teras@iki.fi>
Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
---
 libc/inet/resolv.c |   95 +++++++++++++++++++++++++++-------------------------
 1 files changed, 49 insertions(+), 46 deletions(-)

diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c
index 8781196..07dff18 100644
--- a/libc/inet/resolv.c
+++ b/libc/inet/resolv.c
@@ -456,7 +456,8 @@ extern int __read_etc_hosts_r(parser_t *parser,
 extern int __dns_lookup(const char *name,
 		int type,
 		unsigned char **outpacket,
-		struct resolv_answer *a) attribute_hidden;
+		struct resolv_answer *a,
+		int *h_errnop) attribute_hidden;
 extern int __encode_dotted(const char *dotted,
 		unsigned char *dest,
 		int maxlen) attribute_hidden;
@@ -1233,7 +1234,8 @@ static int __decode_answer(const unsigned char *message, /* packet */
 int attribute_hidden __dns_lookup(const char *name,
 		int type,
 		unsigned char **outpacket,
-		struct resolv_answer *a)
+		struct resolv_answer *a,
+		int *h_errnop)
 {
 	/* Protected by __resolv_lock: */
 	static int last_ns_num = 0;
@@ -1265,11 +1267,15 @@ int attribute_hidden __dns_lookup(const char *name,
 	fd = -1;
 	lookup = NULL;
 	name_len = strlen(name);
-	if ((unsigned)name_len >= MAXDNAME - MAXLEN_searchdomain - 2)
-		goto fail; /* paranoia */
+	if ((unsigned)name_len >= MAXDNAME - MAXLEN_searchdomain - 2) {
+		*h_errnop = NO_RECOVERY;
+		goto fail1; /* paranoia */
+	}
 	lookup = malloc(name_len + 1/*for '.'*/ + MAXLEN_searchdomain + 1);
-	if (!packet || !lookup || !name[0])
-		goto fail;
+	if (!packet || !lookup || !name[0]) {
+		*h_errnop = NO_RECOVERY;
+		goto fail1;
+	}
 	ends_with_dot = (name[name_len - 1] == '.');
 	/* no strcpy! paranoia, user might change name[] under us */
 	memcpy(lookup, name, name_len);
@@ -1337,8 +1343,10 @@ int attribute_hidden __dns_lookup(const char *name,
 		h.rd = 1;
 		DPRINTF("encoding header\n", h.rd);
 		i = __encode_header(&h, packet, PACKETSZ);
-		if (i < 0)
-			goto fail;
+		if (i < 0) {
+			*h_errnop = NO_RECOVERY;
+			goto fail1;
+		}
 
 		/* encode question */
 		DPRINTF("lookup name: %s\n", lookup);
@@ -1346,8 +1354,10 @@ int attribute_hidden __dns_lookup(const char *name,
 		q.qtype = type;
 		q.qclass = C_IN; /* CLASS_IN */
 		j = __encode_question(&q, packet+i, PACKETSZ-i);
-		if (j < 0)
-			goto fail;
+		if (j < 0) {
+			*h_errnop = NO_RECOVERY;
+			goto fail1;
+		}
 		packet_len = i + j;
 
 		/* send packet */
@@ -1473,7 +1483,7 @@ int attribute_hidden __dns_lookup(const char *name,
 				/* no more search domains to try */
 			}
 			/* dont loop, this is "no such host" situation */
-			h_errno = HOST_NOT_FOUND;
+			*h_errnop = HOST_NOT_FOUND;
 			goto fail1;
 		}
 		/* Insert other non-fatal errors here, which do not warrant
@@ -1485,7 +1495,7 @@ int attribute_hidden __dns_lookup(const char *name,
 
 		/* Code below won't work correctly with h.ancount == 0, so... */
 		if (h.ancount <= 0) {
-			h_errno = NO_DATA; /* [is this correct code to check for?] */
+			*h_errnop = NO_DATA; /* [is this correct code to check for?] */
 			goto fail1;
 		}
 		pos = HFIXEDSZ;
@@ -1564,8 +1574,7 @@ int attribute_hidden __dns_lookup(const char *name,
 		variant = -1;
 	} while (retries_left > 0);
 
- fail:
-	h_errno = NETDB_INTERNAL;
+	*h_errnop = TRY_AGAIN;
  fail1:
 	if (fd != -1)
 		close(fd);
@@ -2106,9 +2115,8 @@ int gethostbyname_r(const char *name,
 	 * we'll need space of one in_addr + two addr_list[] elems */
 	a.buflen = buflen - ((sizeof(addr_list[0]) * 2 + sizeof(struct in_addr)));
 	a.add_count = 0;
-	packet_len = __dns_lookup(name, T_A, &packet, &a);
+	packet_len = __dns_lookup(name, T_A, &packet, &a, h_errnop);
 	if (packet_len < 0) {
-		*h_errnop = HOST_NOT_FOUND;
 		DPRINTF("__dns_lookup returned < 0\n");
 		return TRY_AGAIN;
 	}
@@ -2292,9 +2300,8 @@ int gethostbyname2_r(const char *name,
 		int packet_len;
 
 /* Hmm why we memset(a) to zeros only once? */
-		packet_len = __dns_lookup(buf, T_AAAA, &packet, &a);
+		packet_len = __dns_lookup(buf, T_AAAA, &packet, &a, h_errnop);
 		if (packet_len < 0) {
-			*h_errnop = HOST_NOT_FOUND;
 			return TRY_AGAIN;
 		}
 		strncpy(buf, a.dotted, buflen);
@@ -2450,9 +2457,8 @@ int gethostbyaddr_r(const void *addr, socklen_t addrlen,
 	memset(&a, '\0', sizeof(a));
 	for (;;) {
 /* Hmm why we memset(a) to zeros only once? */
-		packet_len = __dns_lookup(buf, T_PTR, &packet, &a);
+		packet_len = __dns_lookup(buf, T_PTR, &packet, &a, h_errnop);
 		if (packet_len < 0) {
-			*h_errnop = HOST_NOT_FOUND;
 			return TRY_AGAIN;
 		}
 
@@ -3091,7 +3097,7 @@ int res_query(const char *dname, int class, int type,
 	}
 
 	memset(&a, '\0', sizeof(a));
-	i = __dns_lookup(dname, type, &packet, &a);
+	i = __dns_lookup(dname, type, &packet, &a, &h_errno);
 
 	if (i < 0) {
 		if (!h_errno) /* TODO: can this ever happen? */
@@ -3117,14 +3123,13 @@ libc_hidden_def(res_query)
  */
 #define __TRAILING_DOT	(1<<0)
 #define __GOT_NODATA	(1<<1)
-#define __GOT_SERVFAIL	(1<<2)
+#define __GOT_TRYAGAIN	(1<<2)
 #define __TRIED_AS_IS	(1<<3)
 int res_search(const char *name, int class, int type, u_char *answer,
 		int anslen)
 {
 	const char *cp;
 	char **domain;
-	HEADER *hp = (HEADER *)(void *)answer;
 	unsigned dots;
 	unsigned state;
 	int ret, saved_herrno;
@@ -3189,19 +3194,9 @@ int res_search(const char *name, int class, int type, u_char *answer,
 			if (ret > 0)
 				return ret;
 
-			/*
-			 * If no server present, give up.
-			 * If name isn't found in this domain,
-			 * keep trying higher domains in the search list
-			 * (if that's enabled).
-			 * On a NO_DATA error, keep trying, otherwise
-			 * a wildcard entry of another type could keep us
-			 * from finding this entry higher in the domain.
-			 * If we get some other error (negative answer or
-			 * server failure), then stop searching up,
-			 * but try the input name below in case it's
-			 * fully-qualified.
-			 */
+			/* our resolver refused to talk to us -
+			 * no sense to retry, as the retry would likely
+			 * fail too */
 			if (errno == ECONNREFUSED) {
 				h_errno = TRY_AGAIN;
 				return -1;
@@ -3209,21 +3204,29 @@ int res_search(const char *name, int class, int type, u_char *answer,
 
 			switch (h_errno) {
 				case NO_DATA:
+					/* Keep trying, otherwise a
+					 * wildcard entry of another type
+					 * could keep us from finding this
+					 * entry from higher in the domain
+					 * search. */
 					state |= __GOT_NODATA;
-					/* FALLTHROUGH */
+					break;
 				case HOST_NOT_FOUND:
-					/* keep trying */
+					/* Not found - keep trying higher
+					 * domains in the search list. */
 					break;
 				case TRY_AGAIN:
-					if (hp->rcode == SERVFAIL) {
-						/* try next search element, if any */
-						state |= __GOT_SERVFAIL;
-						break;
-					}
-					/* FALLTHROUGH */
+					/* Server error or timeout. Could
+					 * be caused by a problem in servers
+					 * our resolver queried. Keep trying
+					 * search, but remember that there
+					 * was a temporary problem. */
+					state |= __GOT_TRYAGAIN;
+					break;
 				default:
 					/* anything else implies that we're done */
 					done = 1;
+					break;
 			}
 			/*
 			 * if we got here for some reason other than DNSRCH,
@@ -3257,13 +3260,13 @@ int res_search(const char *name, int class, int type, u_char *answer,
 		h_errno = saved_herrno;
 	else if (state & __GOT_NODATA)
 		h_errno = NO_DATA;
-	else if (state & __GOT_SERVFAIL)
+	else if (state & __GOT_TRYAGAIN)
 		h_errno = TRY_AGAIN;
 	return -1;
 }
 #undef __TRAILING_DOT
 #undef __GOT_NODATA
-#undef __GOT_SERVFAIL
+#undef __GOT_TRYAGAIN
 #undef __TRIED_AS_IS
 /*
  * Perform a call on res_query on the concatenation of name and domain,
-- 
1.7.8