1 /* dns.c
2  * (c) 2002 Mikulas Patocka
3  * This file is a part of the Links program, released under GPL
4  */
5 
6 #include "links.h"
7 
8 #ifdef SUPPORT_IPV6
9 int support_ipv6;
10 #endif
11 
12 #if !defined(USE_GETADDRINFO) && (defined(HAVE_GETHOSTBYNAME_BUG) || !defined(HAVE_GETHOSTBYNAME))
13 #define EXTERNAL_LOOKUP
14 #endif
15 
16 #if defined(WIN) || defined(INTERIX)
17 #define EXTRA_IPV6_LOOKUP
18 #endif
19 
20 #ifdef OPENVMS_64BIT
21 /* 64-bit getaddrinfo with _SOCKADDR_LEN is broken and returns garbage */
22 #undef addrinfo
23 #undef getaddrinfo
24 #undef freeaddrinfo
25 #define addrinfo        __addrinfo32
my_getaddrinfo(const char * host,const char * service,const struct addrinfo * hints,struct addrinfo ** res)26 static int my_getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct addrinfo **res)
27 {
28 	int r;
29 #pragma __pointer_size	32
30 	char *host_32;
31 	struct addrinfo hints_32;
32 	struct addrinfo *res_32;
33 #pragma __pointer_size	64
34 
35 	host_32 = _malloc32(strlen(host) + 1);
36 	if (!host_32) {
37 		errno = ENOMEM;
38 		return EAI_SYSTEM;
39 	}
40 	strcpy(host_32, host);
41 	memcpy(&hints_32, hints, sizeof(struct addrinfo));
42 
43 	r = __getaddrinfo32(host_32, NULL, &hints_32, &res_32);
44 
45 	free(host_32);
46 
47 	if (!r)
48 		*res = res_32;
49 
50 	return r;
51 }
my_freeaddrinfo(struct addrinfo * res)52 static void my_freeaddrinfo(struct addrinfo *res)
53 {
54 #pragma __pointer_size	32
55 	struct addrinfo *res_32 = (struct addrinfo *)res;
56 #pragma __pointer_size	64
57 	__freeaddrinfo32(res_32);
58 }
59 #define getaddrinfo	my_getaddrinfo
60 #define freeaddrinfo	my_freeaddrinfo
61 #endif
62 
63 struct dnsentry {
64 	list_entry_1st
65 	uttime absolute_time;
66 	struct lookup_result addr;
67 	list_entry_last
68 	unsigned char name[1];
69 };
70 
71 #ifndef THREAD_SAFE_LOOKUP
72 struct dnsquery *dns_queue = NULL;
73 #endif
74 
75 struct dnsquery {
76 #ifndef THREAD_SAFE_LOOKUP
77 	struct dnsquery *next_in_queue;
78 #endif
79 	void (*fn)(void *, int);
80 	void *data;
81 	int h;
82 	struct dnsquery **s;
83 	struct lookup_result *addr;
84 	int addr_preference;
85 	unsigned char name[1];
86 };
87 
88 static int dns_cache_addr_preference = -1;
89 static struct list_head dns_cache = {&dns_cache, &dns_cache};
90 
91 static void end_dns_lookup(struct dnsquery *q, int a);
92 static int shrink_dns_cache(int u);
93 
get_addr_byte(unsigned char ** ptr,unsigned char * res,unsigned char stp)94 static int get_addr_byte(unsigned char **ptr, unsigned char *res, unsigned char stp)
95 {
96 	unsigned u = 0;
97 	if (!(**ptr >= '0' && **ptr <= '9')) return -1;
98 	while (**ptr >= '0' && **ptr <= '9') {
99 		u = u * 10 + **ptr - '0';
100 		if (u >= 256) return -1;
101 		(*ptr)++;
102 	}
103 	if (stp != 255 && **ptr != stp) return -1;
104 	(*ptr)++;
105 	*res = (unsigned char)u;
106 	return 0;
107 }
108 
numeric_ip_address(unsigned char * name,unsigned char address[4])109 int numeric_ip_address(unsigned char *name, unsigned char address[4])
110 {
111 	unsigned char dummy[4];
112 	if (!address) address = dummy;
113 	if (get_addr_byte(&name, address + 0, '.')) return -1;
114 	if (get_addr_byte(&name, address + 1, '.')) return -1;
115 	if (get_addr_byte(&name, address + 2, '.')) return -1;
116 	if (get_addr_byte(&name, address + 3, 0)) return -1;
117 	return 0;
118 }
119 
120 #ifdef SUPPORT_IPV6
121 
extract_ipv6_address(struct addrinfo * p,unsigned char address[16],unsigned * scope_id)122 static int extract_ipv6_address(struct addrinfo *p, unsigned char address[16], unsigned *scope_id)
123 {
124 	/*{
125 		int i;
126 		for (i = 0; i < p->ai_addrlen; i++)
127 			fprintf(stderr, "%02x%c", ((unsigned char *)p->ai_addr)[i], i != p->ai_addrlen - 1 ? ':' : '\n');
128 	}*/
129 	if (p->ai_family == AF_INET6 && (socklen_t)p->ai_addrlen >= (socklen_t)sizeof(struct sockaddr_in6) && p->ai_addr->sa_family == AF_INET6) {
130 		memcpy(address, &((struct sockaddr_in6 *)p->ai_addr)->sin6_addr, 16);
131 #ifdef SUPPORT_IPV6_SCOPE
132 		*scope_id = ((struct sockaddr_in6 *)p->ai_addr)->sin6_scope_id;
133 #else
134 		*scope_id = 0;
135 #endif
136 		return 0;
137 	}
138 	return -1;
139 }
140 
numeric_ipv6_address(unsigned char * name,unsigned char address[16],unsigned * scope_id)141 int numeric_ipv6_address(unsigned char *name, unsigned char address[16], unsigned *scope_id)
142 {
143 	unsigned char dummy_a[16];
144 	unsigned dummy_s;
145 	int r;
146 #ifdef HAVE_INET_PTON
147 	struct in6_addr i6a;
148 #endif
149 	struct addrinfo hints, *res;
150 	if (!address) address = dummy_a;
151 	if (!scope_id) scope_id = &dummy_s;
152 
153 #ifdef HAVE_INET_PTON
154 	if (inet_pton(AF_INET6, cast_const_char name, &i6a) == 1) {
155 		memcpy(address, &i6a, 16);
156 		*scope_id = 0;
157 		return 0;
158 	}
159 	if (!strchr(cast_const_char name, '%'))
160 		return -1;
161 #endif
162 
163 	memset(&hints, 0, sizeof(struct addrinfo));
164 	hints.ai_family = AF_INET6;
165 	hints.ai_flags = AI_NUMERICHOST;
166 	if (getaddrinfo(cast_const_char name, NULL, &hints, &res))
167 		return -1;
168 	r = extract_ipv6_address(res, address, scope_id);
169 	freeaddrinfo(res);
170 	return r;
171 }
172 
173 #endif
174 
175 #ifdef EXTERNAL_LOOKUP
176 
do_external_lookup(unsigned char * name,unsigned char * host)177 static int do_external_lookup(unsigned char *name, unsigned char *host)
178 {
179 	unsigned char buffer[1024];
180 	unsigned char sink[16];
181 	int rd;
182 	int pi[2];
183 	pid_t f;
184 	unsigned char *n;
185 	int rs;
186 	if (c_pipe(pi) == -1)
187 		return -1;
188 	EINTRLOOP(f, fork());
189 	if (f == -1) {
190 		EINTRLOOP(rs, close(pi[0]));
191 		EINTRLOOP(rs, close(pi[1]));
192 		return -1;
193 	}
194 	if (!f) {
195 #ifdef HAVE_SETSID
196 		/* without setsid it gets stuck when on background */
197 		EINTRLOOP(rs, setsid());
198 #endif
199 		EINTRLOOP(rs, close(pi[0]));
200 		EINTRLOOP(rs, dup2(pi[1], 1));
201 		if (rs == -1) _exit(1);
202 		EINTRLOOP(rs, dup2(pi[1], 2));
203 		if (rs == -1) _exit(1);
204 		EINTRLOOP(rs, close(pi[1]));
205 		EINTRLOOP(rs, execlp("host", "host", cast_const_char name, (char *)NULL));
206 		EINTRLOOP(rs, execl("/usr/sbin/host", "host", cast_const_char name, (char *)NULL));
207 		_exit(1);
208 	}
209 	EINTRLOOP(rs, close(pi[1]));
210 	rd = hard_read(pi[0], buffer, sizeof buffer - 1);
211 	if (rd >= 0) buffer[rd] = 0;
212 	if (rd > 0) {
213 		while (hard_read(pi[0], sink, sizeof sink) > 0);
214 	}
215 	EINTRLOOP(rs, close(pi[0]));
216 	/* Don't wait for the process, we already have sigchld handler that
217 	 * does cleanup.
218 	 * waitpid(f, NULL, 0); */
219 	if (rd < 0) return -1;
220 	/*fprintf(stderr, "query: '%s', result: %s\n", name, buffer);*/
221 	while ((n = strstr(buffer, name))) {
222 		memset(n, '-', strlen(cast_const_char name));
223 	}
224 	for (n = buffer; n < buffer + rd; n++) {
225 		if (*n >= '0' && *n <= '9') {
226 			if (get_addr_byte(&n, host + 0, '.')) goto skip_addr;
227 			if (get_addr_byte(&n, host + 1, '.')) goto skip_addr;
228 			if (get_addr_byte(&n, host + 2, '.')) goto skip_addr;
229 			if (get_addr_byte(&n, host + 3, 255)) goto skip_addr;
230 			return 0;
231 skip_addr:
232 			if (n >= buffer + rd) break;
233 		}
234 	}
235 	return -1;
236 }
237 
238 #endif
239 
240 #if MAX_ADDRESSES > 1
memcmp_host_address(struct host_address * a,struct host_address * b)241 static int memcmp_host_address(struct host_address *a, struct host_address *b)
242 {
243 	if (a->af != b->af || a->scope_id != b->scope_id)
244 		return 1;
245 	return memcmp(a->addr, b->addr, sizeof a->addr);
246 }
247 #endif
248 
add_address(struct lookup_result * host,int af,unsigned char * address,unsigned scope_id,int preference)249 static void add_address(struct lookup_result *host, int af, unsigned char *address, unsigned scope_id, int preference)
250 {
251 	struct host_address neww;
252 	struct host_address *e, *t;
253 #if MAX_ADDRESSES > 1
254 	struct host_address *n;
255 #endif
256 	if (af != AF_INET && preference == ADDR_PREFERENCE_IPV4_ONLY)
257 		return;
258 #ifdef SUPPORT_IPV6
259 	if (af != AF_INET6 && preference == ADDR_PREFERENCE_IPV6_ONLY)
260 		return;
261 #endif
262 	if (host->n >= MAX_ADDRESSES)
263 		return;
264 	memset(&neww, 0, sizeof(struct host_address));
265 	neww.af = af;
266 	memcpy(neww.addr, address, af == AF_INET ? 4 : 16);
267 	neww.scope_id = scope_id;
268 	e = &host->a[host->n];
269 	t = e;
270 #if MAX_ADDRESSES > 1
271 	for (n = host->a; n != e; n++) {
272 		if (!memcmp_host_address(n, &neww))
273 			return;
274 		if (preference == ADDR_PREFERENCE_IPV4 && af == AF_INET && n->af != AF_INET) {
275 			t = n;
276 			break;
277 		}
278 #ifdef SUPPORT_IPV6
279 		if (preference == ADDR_PREFERENCE_IPV6 && af == AF_INET6 && n->af != AF_INET6) {
280 			t = n;
281 			break;
282 		}
283 #endif
284 	}
285 	memmove(t + 1, t, (e - t) * sizeof(struct host_address));
286 #endif
287 	memcpy(t, &neww, sizeof(struct host_address));
288 	host->n++;
289 }
290 
291 #ifdef USE_GETADDRINFO
292 
use_getaddrinfo(unsigned char * name,struct addrinfo * hints,int preference,struct lookup_result * host)293 static int use_getaddrinfo(unsigned char *name, struct addrinfo *hints, int preference, struct lookup_result *host)
294 {
295 	int gai_err;
296 	struct addrinfo *res, *p;
297 #ifdef OPENVMS
298 	struct addrinfo default_hints;
299 	if (!hints) {
300 		memset(&default_hints, 0, sizeof default_hints);
301 		default_hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
302 		hints = &default_hints;
303 	}
304 #endif
305 	gai_err = getaddrinfo(cast_const_char name, NULL, hints, &res);
306 	if (gai_err)
307 		return gai_err;
308 	for (p = res; p; p = p->ai_next) {
309 		if (p->ai_family == AF_INET && (socklen_t)p->ai_addrlen >= (socklen_t)sizeof(struct sockaddr_in) && p->ai_addr->sa_family == AF_INET) {
310 			add_address(host, AF_INET, (unsigned char *)&((struct sockaddr_in *)p->ai_addr)->sin_addr.s_addr, 0, preference);
311 			continue;
312 		}
313 #ifdef SUPPORT_IPV6
314 		{
315 			unsigned char address[16];
316 			unsigned scope_id;
317 			if (!extract_ipv6_address(p, address, &scope_id)) {
318 				add_address(host, AF_INET6, address, scope_id, preference);
319 				continue;
320 			}
321 		}
322 #endif
323 	}
324 	freeaddrinfo(res);
325 	return 0;
326 }
327 
328 #endif
329 
rotate_addresses(struct lookup_result * host)330 void rotate_addresses(struct lookup_result *host)
331 {
332 #if MAX_ADDRESSES > 1
333 	int first_type, first_different, i;
334 
335 	if (host->n <= 2)
336 		return;
337 
338 	first_type = host->a[0].af;
339 
340 	for (i = 1; i < host->n; i++) {
341 		if (host->a[i].af != first_type) {
342 			first_different = i;
343 			goto do_swap;
344 		}
345 	}
346 	return;
347 
348 do_swap:
349 	if (first_different > 1) {
350 		struct host_address ha;
351 		memcpy(&ha, &host->a[first_different], sizeof(struct host_address));
352 		memmove(&host->a[2], &host->a[1], (first_different - 1) * sizeof(struct host_address));
353 		memcpy(&host->a[1], &ha, sizeof(struct host_address));
354 	}
355 #endif
356 }
357 
do_real_lookup(unsigned char * name,int preference,struct lookup_result * host)358 void do_real_lookup(unsigned char *name, int preference, struct lookup_result *host)
359 {
360 	unsigned char address[16];
361 #ifdef SUPPORT_IPV6
362 	size_t nl;
363 #endif
364 
365 	memset(host, 0, sizeof(struct lookup_result));
366 
367 	if (strlen(cast_const_char name) >= 6 && !casestrcmp(name + strlen(cast_const_char name) - 6, cast_uchar ".onion"))
368 		goto ret;
369 
370 	if (!support_ipv6) preference = ADDR_PREFERENCE_IPV4_ONLY;
371 
372 	if (!numeric_ip_address(name, address)) {
373 		add_address(host, AF_INET, address, 0, preference);
374 		goto ret;
375 	}
376 #ifdef SUPPORT_IPV6
377 	nl = strlen(cast_const_char name);
378 	if (name[0] == '[' && name[nl - 1] == ']') {
379 		unsigned char *n2 = cast_uchar strdup(cast_const_char(name + 1));
380 		if (n2) {
381 			unsigned scope_id;
382 			n2[nl - 2] = 0;
383 			if (!numeric_ipv6_address(n2, address, &scope_id)) {
384 				free(n2);
385 				add_address(host, AF_INET6, address, scope_id, preference);
386 				goto ret;
387 			}
388 			free(n2);
389 		}
390 	} else {
391 		unsigned scope_id;
392 		if (!numeric_ipv6_address(name, address, &scope_id)) {
393 			add_address(host, AF_INET6, address, scope_id, preference);
394 			goto ret;
395 		}
396 	}
397 #endif
398 
399 #if defined(USE_GETADDRINFO)
400 	use_getaddrinfo(name, NULL, preference, host);
401 #if defined(SUPPORT_IPV6) && defined(EXTRA_IPV6_LOOKUP)
402 	if ((preference == ADDR_PREFERENCE_IPV4 && !host->n) ||
403 	    preference == ADDR_PREFERENCE_IPV6 ||
404 	    preference == ADDR_PREFERENCE_IPV6_ONLY) {
405 		struct addrinfo hints;
406 		int i;
407 		for (i = 0; i < host->n; i++)
408 			if (host->a[i].af == AF_INET6)
409 				goto already_have_inet6;
410 		memset(&hints, 0, sizeof hints);
411 		hints.ai_family = AF_INET6;
412 		hints.ai_flags = 0;
413 		use_getaddrinfo(name, &hints, preference, host);
414 	}
415 	already_have_inet6:;
416 #endif
417 #elif defined(HAVE_GETHOSTBYNAME)
418 	{
419 		int i;
420 		struct hostent *hst;
421 		if ((hst = gethostbyname(cast_const_char name))) {
422 			if (hst->h_addrtype != AF_INET || hst->h_length != 4 || !hst->h_addr)
423 				goto ret;
424 #ifdef h_addr
425 			for (i = 0; hst->h_addr_list[i]; i++) {
426 				add_address(host, AF_INET, cast_uchar hst->h_addr_list[i], 0, preference);
427 			}
428 #else
429 			add_address(host, AF_INET, cast_uchar hst->h_addr, 0, preference);
430 #endif
431 			goto ret;
432 		}
433 	}
434 #endif
435 
436 #ifdef EXTERNAL_LOOKUP
437 	if (!do_external_lookup(name, address)) {
438 		add_address(host, AF_INET, address, 0, preference);
439 		goto ret;
440 	}
441 #endif
442 
443 ret:
444 	return;
445 }
446 
447 #ifndef NO_ASYNC_LOOKUP
lookup_fn(void * q_,int h)448 static void lookup_fn(void *q_, int h)
449 {
450 	struct dnsquery *q = (struct dnsquery *)q_;
451 	struct lookup_result host;
452 	do_real_lookup(q->name, q->addr_preference, &host);
453 	/*{
454 		int i;
455 		for (i = 0; i < sizeof(struct lookup_result); i++) {
456 			if (i == 1) portable_sleep(1000);
457 			hard_write(h, (unsigned char *)&host + i, 1);
458 		}
459 	}*/
460 	hard_write(h, (unsigned char *)&host, sizeof(struct lookup_result));
461 }
462 
end_real_lookup(void * q_)463 static void end_real_lookup(void *q_)
464 {
465 	struct dnsquery *q = (struct dnsquery *)q_;
466 	int r = 1;
467 	int rs;
468 	if (!q->addr || hard_read(q->h, (unsigned char *)q->addr, sizeof(struct lookup_result)) != sizeof(struct lookup_result) || !q->addr->n) goto end;
469 	r = 0;
470 
471 	end:
472 	set_handlers(q->h, NULL, NULL, NULL);
473 	EINTRLOOP(rs, close(q->h));
474 	end_dns_lookup(q, r);
475 }
476 #endif
477 
do_lookup(struct dnsquery * q,int force_async)478 static int do_lookup(struct dnsquery *q, int force_async)
479 {
480 	/*debug("starting lookup for %s", q->name);*/
481 #ifndef NO_ASYNC_LOOKUP
482 	if (!async_lookup && !force_async) {
483 #endif
484 #ifndef NO_ASYNC_LOOKUP
485 		sync_lookup:
486 #endif
487 		do_real_lookup(q->name, q->addr_preference, q->addr);
488 		end_dns_lookup(q, !q->addr->n);
489 		return 0;
490 #ifndef NO_ASYNC_LOOKUP
491 	} else {
492 		q->h = start_thread(lookup_fn, q, (int)((unsigned char *)strchr(cast_const_char q->name, 0) + 1 - (unsigned char *)q), 1);
493 		if (q->h == -1) goto sync_lookup;
494 		set_handlers(q->h, end_real_lookup, NULL, q);
495 		return 1;
496 	}
497 #endif
498 }
499 
do_queued_lookup(struct dnsquery * q)500 static int do_queued_lookup(struct dnsquery *q)
501 {
502 #ifndef THREAD_SAFE_LOOKUP
503 	q->next_in_queue = NULL;
504 	if (!dns_queue) {
505 		dns_queue = q;
506 		/*debug("direct lookup");*/
507 #endif
508 		return do_lookup(q, 0);
509 #ifndef THREAD_SAFE_LOOKUP
510 	} else {
511 		/*debug("queuing lookup for %s", q->name);*/
512 		if (dns_queue->next_in_queue) internal_error("DNS queue corrupted");
513 		dns_queue->next_in_queue = q;
514 		dns_queue = q;
515 		return 1;
516 	}
517 #endif
518 }
519 
check_dns_cache_addr_preference(void)520 static void check_dns_cache_addr_preference(void)
521 {
522 	if (dns_cache_addr_preference != ipv6_options.addr_preference) {
523 		shrink_dns_cache(SH_FREE_ALL);
524 		dns_cache_addr_preference = ipv6_options.addr_preference;
525 	}
526 }
527 
find_in_dns_cache(unsigned char * name,struct dnsentry ** dnsentry)528 static int find_in_dns_cache(unsigned char *name, struct dnsentry **dnsentry)
529 {
530 	struct dnsentry *e;
531 	struct list_head *le;
532 	check_dns_cache_addr_preference();
533 	foreach(struct dnsentry, e, le, dns_cache)
534 		if (!casestrcmp(e->name, name)) {
535 			del_from_list(e);
536 			add_to_list(dns_cache, e);
537 			*dnsentry = e;
538 			return 0;
539 		}
540 	return -1;
541 }
542 
free_dns_entry(struct dnsentry * dnsentry)543 static void free_dns_entry(struct dnsentry *dnsentry)
544 {
545 	del_from_list(dnsentry);
546 	mem_free(dnsentry);
547 }
548 
end_dns_lookup(struct dnsquery * q,int a)549 static void end_dns_lookup(struct dnsquery *q, int a)
550 {
551 	struct dnsentry *dnsentry;
552 	size_t sl;
553 	void (*fn)(void *, int);
554 	void *data;
555 	/*debug("end lookup %s", q->name);*/
556 #ifndef THREAD_SAFE_LOOKUP
557 	if (q->next_in_queue) {
558 		/*debug("processing next in queue: %s", q->next_in_queue->name);*/
559 		do_lookup(q->next_in_queue, 1);
560 	} else dns_queue = NULL;
561 #endif
562 	if (!q->fn || !q->addr) {
563 		free(q);
564 		return;
565 	}
566 	if (!find_in_dns_cache(q->name, &dnsentry)) {
567 		if (a) {
568 			memcpy(q->addr, &dnsentry->addr, sizeof(struct lookup_result));
569 			a = 0;
570 			goto e;
571 		}
572 		free_dns_entry(dnsentry);
573 	}
574 	if (a) goto e;
575 	if (q->addr_preference != ipv6_options.addr_preference) goto e;
576 	check_dns_cache_addr_preference();
577 	sl = strlen(cast_const_char q->name);
578 	if (sl > MAXINT - sizeof(struct dnsentry)) overalloc();
579 	dnsentry = mem_alloc(sizeof(struct dnsentry) + sl);
580 	strcpy(cast_char dnsentry->name, cast_const_char q->name);
581 	memcpy(&dnsentry->addr, q->addr, sizeof(struct lookup_result));
582 	dnsentry->absolute_time = get_absolute_time();
583 	add_to_list(dns_cache, dnsentry);
584 	e:
585 	if (q->s) *q->s = NULL;
586 	fn = q->fn;
587 	data = q->data;
588 	free(q);
589 	fn(data, a);
590 }
591 
find_host_no_cache(unsigned char * name,struct lookup_result * addr,void ** qp,void (* fn)(void *,int),void * data)592 int find_host_no_cache(unsigned char *name, struct lookup_result *addr, void **qp, void (*fn)(void *, int), void *data)
593 {
594 	struct dnsquery *q;
595 	retry:
596 	q = (struct dnsquery *)malloc(sizeof(struct dnsquery) + strlen(cast_const_char name));
597 	if (!q) {
598 		if (out_of_memory(0, NULL, 0))
599 			goto retry;
600 		fn(data, 1);
601 		return 0;
602 	}
603 	q->fn = fn;
604 	q->data = data;
605 	q->s = (struct dnsquery **)qp;
606 	q->addr = addr;
607 	q->addr_preference = ipv6_options.addr_preference;
608 	strcpy(cast_char q->name, cast_const_char name);
609 	if (qp) *qp = q;
610 	return do_queued_lookup(q);
611 }
612 
find_host(unsigned char * name,struct lookup_result * addr,void ** qp,void (* fn)(void *,int),void * data)613 int find_host(unsigned char *name, struct lookup_result *addr, void **qp, void (*fn)(void *, int), void *data)
614 {
615 	struct dnsentry *dnsentry;
616 	if (qp) *qp = NULL;
617 	if (!find_in_dns_cache(name, &dnsentry)) {
618 		if (get_absolute_time() - dnsentry->absolute_time > DNS_TIMEOUT) goto timeout;
619 		memcpy(addr, &dnsentry->addr, sizeof(struct lookup_result));
620 		fn(data, 0);
621 		return 0;
622 	}
623 	timeout:
624 	return find_host_no_cache(name, addr, qp, fn, data);
625 }
626 
kill_dns_request(void ** qp)627 void kill_dns_request(void **qp)
628 {
629 	struct dnsquery *q = *qp;
630 	q->fn = NULL;
631 	q->addr = NULL;
632 	*qp = NULL;
633 }
634 
635 #ifndef NO_ASYNC_LOOKUP
dns_prefetch_end(void * addr_,int status)636 static void dns_prefetch_end(void *addr_, int status)
637 {
638 	struct lookup_result *addr = (struct lookup_result *)addr_;
639 	free(addr);
640 }
641 #endif
642 
dns_prefetch(unsigned char * name)643 void dns_prefetch(unsigned char *name)
644 {
645 #ifndef NO_ASYNC_LOOKUP
646 	struct lookup_result *addr;
647 	if (!async_lookup)
648 		return;
649 	addr = (struct lookup_result *)malloc(sizeof(struct lookup_result));
650 	if (!addr)
651 		return;
652 	find_host(name, addr, NULL, dns_prefetch_end, addr);
653 #endif
654 }
655 
656 #if MAX_ADDRESSES > 1
dns_set_priority(unsigned char * name,struct host_address * address,int prefer)657 void dns_set_priority(unsigned char *name, struct host_address *address, int prefer)
658 {
659 	int i;
660 	struct dnsentry *dnsentry;
661 	if (find_in_dns_cache(name, &dnsentry))
662 		return;
663 	for (i = 0; i < dnsentry->addr.n; i++)
664 		if (!memcmp_host_address(&dnsentry->addr.a[i], address)) goto found_it;
665 	return;
666 	found_it:
667 	if (prefer) {
668 		memmove(&dnsentry->addr.a[1], &dnsentry->addr.a[0], i * sizeof(struct host_address));
669 		memcpy(&dnsentry->addr.a[0], address, sizeof(struct host_address));
670 	} else {
671 		memmove(&dnsentry->addr.a[i], &dnsentry->addr.a[i + 1], (dnsentry->addr.n - i - 1) * sizeof(struct host_address));
672 		memcpy(&dnsentry->addr.a[dnsentry->addr.n - 1], address, sizeof(struct host_address));
673 	}
674 }
675 #endif
676 
dns_clear_host(unsigned char * name)677 void dns_clear_host(unsigned char *name)
678 {
679 	struct dnsentry *dnsentry;
680 	if (find_in_dns_cache(name, &dnsentry))
681 		return;
682 	free_dns_entry(dnsentry);
683 }
684 
dns_info(int type)685 unsigned long dns_info(int type)
686 {
687 	switch (type) {
688 		case CI_FILES:
689 			return list_size(&dns_cache);
690 		default:
691 			internal_error("dns_info: bad request");
692 	}
693 	return 0;
694 }
695 
shrink_dns_cache(int u)696 static int shrink_dns_cache(int u)
697 {
698 	uttime now = get_absolute_time();
699 	struct dnsentry *d;
700 	struct list_head *ld;
701 	int f = 0;
702 	if (u == SH_FREE_SOMETHING && !list_empty(dns_cache)) {
703 		d = list_struct(dns_cache.prev, struct dnsentry);
704 		goto delete_last;
705 	}
706 	foreach(struct dnsentry, d, ld, dns_cache) if (u == SH_FREE_ALL || now - d->absolute_time > DNS_TIMEOUT) {
707 delete_last:
708 		ld = d->list_entry.prev;
709 		free_dns_entry(d);
710 		f = ST_SOMETHING_FREED;
711 	}
712 	return f | (list_empty(dns_cache) ? ST_CACHE_EMPTY : 0);
713 }
714 
print_address(struct host_address * a)715 unsigned char *print_address(struct host_address *a)
716 {
717 #define SCOPE_ID_LEN	11
718 #ifdef SUPPORT_IPV6
719 	static unsigned char buffer[INET6_ADDRSTRLEN + SCOPE_ID_LEN];
720 #else
721 	static unsigned char buffer[INET_ADDRSTRLEN + SCOPE_ID_LEN];
722 #endif
723 #ifdef HAVE_INET_NTOP
724 	union {
725 		struct in_addr in;
726 #ifdef SUPPORT_IPV6
727 		struct in6_addr in6;
728 #endif
729 		char pad[16];
730 	} u;
731 	memcpy(&u, a->addr, 16);
732 	if (!inet_ntop(a->af, &u, cast_char buffer, sizeof buffer - SCOPE_ID_LEN))
733 		return NULL;
734 #else
735 	if (a->af == AF_INET)
736 		snprintf(cast_char buffer, sizeof buffer, "%d.%d.%d.%d", a->addr[0], a->addr[1], a->addr[2], a->addr[3]);
737 #ifdef SUPPORT_IPV6
738 	else if (a->af == AF_INET6)
739 		snprintf(cast_char buffer, sizeof buffer, "%x:%x:%x:%x:%x:%x:%x:%x",
740 			(a->addr[0] << 8) | a->addr[1],
741 			(a->addr[2] << 8) | a->addr[3],
742 			(a->addr[4] << 8) | a->addr[5],
743 			(a->addr[6] << 8) | a->addr[7],
744 			(a->addr[8] << 8) | a->addr[9],
745 			(a->addr[10] << 8) | a->addr[11],
746 			(a->addr[12] << 8) | a->addr[13],
747 			(a->addr[14] << 8) | a->addr[15]);
748 #endif
749 	else
750 		return NULL;
751 #endif
752 	if (a->scope_id) {
753 		unsigned char *end = cast_uchar strchr(cast_const_char buffer, 0);
754 		snprintf(cast_char end, buffer + sizeof(buffer) - end, "%%%u", a->scope_id);
755 	}
756 	return buffer;
757 }
758 
ipv6_full_access(void)759 int ipv6_full_access(void)
760 {
761 #ifdef SUPPORT_IPV6
762 	/*
763 	 * Test if we can access global IPv6 address space.
764 	 * This doesn't send anything anywhere, it just creates an UDP socket,
765 	 * connects it and closes it.
766 	 */
767 	struct sockaddr_in6 sin6;
768 	int h, c, rs;
769 	if (!support_ipv6) return 0;
770 	h = c_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
771 	if (h == -1) return 0;
772 	memset(&sin6, 0, sizeof sin6);
773 	sin6.sin6_family = AF_INET6;
774 	sin6.sin6_port = htons(1024);
775 	memcpy(&sin6.sin6_addr.s6_addr, "\052\001\004\060\000\015\000\000\002\314\236\377\376\044\176\032", 16);
776 	EINTRLOOP(c, connect(h, (struct sockaddr *)(void *)&sin6, sizeof sin6));
777 	EINTRLOOP(rs, close(h));
778 	if (!c) return 1;
779 #endif
780 	return 0;
781 }
782 
783 #ifdef FLOOD_MEMORY
784 
flood_memory(void)785 void flood_memory(void)
786 {
787 	struct list_head list;
788 	size_t s = 32768 * 32;
789 	struct dnsentry *de;
790 	dns_cache_addr_preference = ipv6_options.addr_preference;
791 #if defined(HAVE__HEAPMIN)
792 	_heapmin();
793 #endif
794 	init_list(list);
795 	while (!list_empty(dns_cache)) {
796 		de = list_struct(dns_cache.prev, struct dnsentry);
797 		del_from_list(de);
798 		add_to_list(list, de);
799 	}
800 	while (1) {
801 		while ((de = mem_alloc_mayfail(s))) {
802 			de->absolute_time = get_absolute_time();
803 			memset(&de->addr, 0, sizeof de->addr);
804 			de->name[0] = 0;
805 			add_to_list(list, de);
806 		}
807 		if (s == sizeof(struct dnsentry)) break;
808 		s = s / 2;
809 		if (s < sizeof(struct dnsentry)) s = sizeof(struct dnsentry);
810 	}
811 	while (!list_empty(list)) {
812 		de = list_struct(list.prev, struct dnsentry);
813 		del_from_list(de);
814 		add_to_list(dns_cache, de);
815 	}
816 }
817 
818 #endif
819 
init_dns(void)820 void init_dns(void)
821 {
822 	register_cache_upcall(shrink_dns_cache, 0, cast_uchar "dns");
823 #ifdef FLOOD_MEMORY
824 	flood_memory();
825 #endif
826 #ifdef SUPPORT_IPV6
827 	{
828 		int h, rs;
829 		h = c_socket(AF_INET6, SOCK_STREAM, 0);
830 		if (h == -1) {
831 			support_ipv6 = 0;
832 		} else {
833 			EINTRLOOP(rs, close(h));
834 			support_ipv6 = 1;
835 		}
836 	}
837 #endif
838 }
839