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