dns.c (12317B)
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 "config.h" 7 #include "links.h" 8 9 struct dnsentry { 10 list_entry_1st; 11 uttime absolute_time; 12 struct lookup_result addr; 13 char name[1]; 14 }; 15 16 struct dnsquery { 17 void (*fn)(void *, int); 18 void *data; 19 int h; 20 struct dnsquery **s; 21 struct lookup_result *addr; 22 int addr_preference; 23 char name[]; 24 }; 25 26 static int dns_cache_addr_preference = -1; 27 static struct list_head dns_cache = { &dns_cache, &dns_cache }; 28 29 static void end_dns_lookup(struct dnsquery *q, int a); 30 static int shrink_dns_cache(int u); 31 32 static int 33 get_addr_byte(const char *p, char *res, const char stp) 34 { 35 char u = 0; 36 if (!(*p >= '0' && *p <= '9')) 37 return -1; 38 for (; *p >= '0' && *p <= '9'; p++) { 39 if (u * 10 + *p - '0' > 255) 40 return -1; 41 u = u * 10 + *p - '0'; 42 } 43 if (*p != stp) 44 return -1; 45 p++; 46 *res = u; 47 return 0; 48 } 49 50 int 51 numeric_ip_address(const char *name, char address[4]) 52 { 53 char dummy[4]; 54 if (!address) 55 address = dummy; 56 if (get_addr_byte(name, address + 0, '.') 57 || get_addr_byte(name, address + 1, '.') 58 || get_addr_byte(name, address + 2, '.') 59 || get_addr_byte(name, address + 3, 0)) 60 return -1; 61 return 0; 62 } 63 64 static int 65 extract_ipv6_address(struct addrinfo *p, char address[16], unsigned *scope_id) 66 { 67 if (p->ai_family == AF_INET6 68 && (socklen_t)p->ai_addrlen 69 >= (socklen_t)sizeof(struct sockaddr_in6) 70 && p->ai_addr->sa_family == AF_INET6) { 71 memcpy(address, &((struct sockaddr_in6 *)p->ai_addr)->sin6_addr, 72 16); 73 *scope_id = ((struct sockaddr_in6 *)p->ai_addr)->sin6_scope_id; 74 return 0; 75 } 76 return -1; 77 } 78 79 int 80 numeric_ipv6_address(const char *name, char address[16], unsigned *scope_id) 81 { 82 char dummy_a[16]; 83 unsigned dummy_s; 84 int r; 85 struct in6_addr i6a; 86 struct addrinfo hints, *res; 87 if (!address) 88 address = dummy_a; 89 if (!scope_id) 90 scope_id = &dummy_s; 91 92 if (inet_pton(AF_INET6, name, &i6a) == 1) { 93 memcpy(address, &i6a, 16); 94 *scope_id = 0; 95 return 0; 96 } 97 if (!strchr(cast_const_char name, '%')) 98 return -1; 99 100 memset(&hints, 0, sizeof(struct addrinfo)); 101 hints.ai_family = AF_INET6; 102 hints.ai_flags = AI_NUMERICHOST; 103 if (getaddrinfo(name, NULL, &hints, &res)) 104 return -1; 105 r = extract_ipv6_address(res, address, scope_id); 106 freeaddrinfo(res); 107 return r; 108 } 109 110 #if MAX_ADDRESSES > 1 111 static int 112 memcmp_host_address(struct host_address *a, struct host_address *b) 113 { 114 if (a->af != b->af || a->scope_id != b->scope_id) 115 return 1; 116 return memcmp(a->addr, b->addr, sizeof a->addr); 117 } 118 #endif 119 120 static void 121 add_address(struct lookup_result *host, int af, unsigned char *address, 122 unsigned scope_id, int preference) 123 { 124 struct host_address neww; 125 struct host_address *e, *t; 126 #if MAX_ADDRESSES > 1 127 struct host_address *n; 128 #endif 129 if ((af != AF_INET && preference == ADDR_PREFERENCE_IPV4_ONLY) 130 || (af != AF_INET6 && preference == ADDR_PREFERENCE_IPV6_ONLY) 131 || (host->n >= MAX_ADDRESSES)) 132 return; 133 memset(&neww, 0, sizeof(struct host_address)); 134 neww.af = af; 135 memcpy(neww.addr, address, af == AF_INET ? 4 : 16); 136 neww.scope_id = scope_id; 137 e = &host->a[host->n]; 138 t = e; 139 #if MAX_ADDRESSES > 1 140 for (n = host->a; n != e; n++) { 141 if (!memcmp_host_address(n, &neww)) 142 return; 143 if ((preference == ADDR_PREFERENCE_IPV4 && af == AF_INET 144 && n->af != AF_INET) 145 || (preference == ADDR_PREFERENCE_IPV6 && af == AF_INET6 146 && n->af != AF_INET6)) { 147 t = n; 148 break; 149 } 150 } 151 memmove(t + 1, t, (e - t) * sizeof(struct host_address)); 152 #endif 153 memcpy(t, &neww, sizeof(struct host_address)); 154 host->n++; 155 } 156 157 static int 158 use_getaddrinfo(unsigned char *name, struct addrinfo *hints, int preference, 159 struct lookup_result *host) 160 { 161 int gai_err; 162 struct addrinfo *res, *p; 163 gai_err = getaddrinfo(cast_const_char name, NULL, hints, &res); 164 if (gai_err) 165 return gai_err; 166 for (p = res; p; p = p->ai_next) { 167 if (p->ai_family == AF_INET 168 && (socklen_t)p->ai_addrlen 169 >= (socklen_t)sizeof(struct sockaddr_in) 170 && p->ai_addr->sa_family == AF_INET) { 171 add_address( 172 host, AF_INET, 173 (unsigned char *)&((struct sockaddr_in *)p->ai_addr) 174 ->sin_addr.s_addr, 175 0, preference); 176 continue; 177 } 178 { 179 unsigned char address[16]; 180 unsigned scope_id; 181 if (!extract_ipv6_address(p, (char *)address, 182 &scope_id)) { 183 add_address(host, AF_INET6, address, scope_id, 184 preference); 185 continue; 186 } 187 } 188 } 189 freeaddrinfo(res); 190 return 0; 191 } 192 193 void 194 rotate_addresses(struct lookup_result *host) 195 { 196 #if MAX_ADDRESSES > 1 197 int first_type, first_different, i; 198 199 if (host->n <= 2) 200 return; 201 202 first_type = host->a[0].af; 203 204 for (i = 1; i < host->n; i++) { 205 if (host->a[i].af != first_type) { 206 first_different = i; 207 goto do_swap; 208 } 209 } 210 return; 211 212 do_swap: 213 if (first_different > 1) { 214 struct host_address ha; 215 memcpy(&ha, &host->a[first_different], 216 sizeof(struct host_address)); 217 memmove(&host->a[2], &host->a[1], 218 (first_different - 1) * sizeof(struct host_address)); 219 memcpy(&host->a[1], &ha, sizeof(struct host_address)); 220 } 221 #endif 222 } 223 224 static void 225 do_real_lookup(unsigned char *name, int preference, struct lookup_result *host) 226 { 227 unsigned char address[16]; 228 size_t nl; 229 230 memset(host, 0, sizeof(struct lookup_result)); 231 232 if (strlen(cast_const_char name) >= 6 233 && !casestrcmp(name + strlen(cast_const_char name) - 6, 234 cast_uchar ".onion")) 235 goto ret; 236 237 if (!support_ipv6) 238 preference = ADDR_PREFERENCE_IPV4_ONLY; 239 240 if (!numeric_ip_address((char *)name, (char *)address)) { 241 add_address(host, AF_INET, address, 0, preference); 242 goto ret; 243 } 244 nl = strlen(cast_const_char name); 245 if (name[0] == '[' && name[nl - 1] == ']') { 246 unsigned char *n2 = 247 cast_uchar strdup(cast_const_char(name + 1)); 248 if (n2) { 249 unsigned scope_id; 250 n2[nl - 2] = 0; 251 if (!numeric_ipv6_address((char *)n2, (char *)address, 252 &scope_id)) { 253 free(n2); 254 add_address(host, AF_INET6, address, scope_id, 255 preference); 256 goto ret; 257 } 258 free(n2); 259 } 260 } else { 261 unsigned scope_id; 262 if (!numeric_ipv6_address((char *)name, (char *)address, 263 &scope_id)) { 264 add_address(host, AF_INET6, address, scope_id, 265 preference); 266 goto ret; 267 } 268 } 269 270 use_getaddrinfo(name, NULL, preference, host); 271 #if defined(EXTRA_IPV6_LOOKUP) 272 if ((preference == ADDR_PREFERENCE_IPV4 && !host->n) 273 || preference == ADDR_PREFERENCE_IPV6 274 || preference == ADDR_PREFERENCE_IPV6_ONLY) { 275 struct addrinfo hints; 276 int i; 277 for (i = 0; i < host->n; i++) 278 if (host->a[i].af == AF_INET6) 279 goto already_have_inet6; 280 memset(&hints, 0, sizeof hints); 281 hints.ai_family = AF_INET6; 282 hints.ai_flags = 0; 283 use_getaddrinfo(name, &hints, preference, host); 284 } 285 already_have_inet6:; 286 #endif 287 ret: 288 return; 289 } 290 291 static int 292 do_lookup(struct dnsquery *q) 293 { 294 do_real_lookup((unsigned char *)q->name, q->addr_preference, q->addr); 295 end_dns_lookup(q, !q->addr->n); 296 return 0; 297 } 298 299 static void 300 check_dns_cache_addr_preference(void) 301 { 302 if (dns_cache_addr_preference != ipv6_options.addr_preference) { 303 shrink_dns_cache(SH_FREE_ALL); 304 dns_cache_addr_preference = ipv6_options.addr_preference; 305 } 306 } 307 308 static int 309 find_in_dns_cache(char *name, struct dnsentry **dnsentry) 310 { 311 struct dnsentry *e = NULL; 312 struct list_head *le; 313 check_dns_cache_addr_preference(); 314 foreach (struct dnsentry, e, le, dns_cache) 315 if (!strcasecmp(e->name, name)) { 316 del_from_list(e); 317 add_to_list(dns_cache, e); 318 *dnsentry = e; 319 return 0; 320 } 321 return -1; 322 } 323 324 static void 325 free_dns_entry(struct dnsentry *dnsentry) 326 { 327 del_from_list(dnsentry); 328 free(dnsentry); 329 } 330 331 static void 332 end_dns_lookup(struct dnsquery *q, int a) 333 { 334 struct dnsentry *dnsentry; 335 void (*fn)(void *, int); 336 void *data; 337 if (!q->fn || !q->addr) { 338 free(q); 339 return; 340 } 341 if (!find_in_dns_cache(q->name, &dnsentry)) { 342 if (a) { 343 memcpy(q->addr, &dnsentry->addr, 344 sizeof(struct lookup_result)); 345 a = 0; 346 goto e; 347 } 348 free_dns_entry(dnsentry); 349 } 350 if (a) 351 goto e; 352 if (q->addr_preference != ipv6_options.addr_preference) 353 goto e; 354 check_dns_cache_addr_preference(); 355 dnsentry = xmalloc(sizeof(struct dnsentry) + strlen(q->name)); 356 strcpy(dnsentry->name, q->name); 357 memcpy(&dnsentry->addr, q->addr, sizeof(struct lookup_result)); 358 dnsentry->absolute_time = get_absolute_time(); 359 add_to_list(dns_cache, dnsentry); 360 e: 361 if (q->s) 362 *q->s = NULL; 363 fn = q->fn; 364 data = q->data; 365 free(q); 366 fn(data, a); 367 } 368 369 int 370 find_host_no_cache(char *name, struct lookup_result *addr, void **qp, 371 void (*fn)(void *, int), void *data) 372 { 373 struct dnsquery *q; 374 q = xmalloc(sizeof(struct dnsquery) + strlen(name)); 375 q->fn = fn; 376 q->data = data; 377 q->s = (struct dnsquery **)qp; 378 q->addr = addr; 379 q->addr_preference = ipv6_options.addr_preference; 380 strcpy(q->name, name); 381 if (qp) 382 *qp = q; 383 return do_lookup(q); 384 } 385 386 int 387 find_host(char *name, struct lookup_result *addr, void **qp, 388 void (*fn)(void *, int), void *data) 389 { 390 struct dnsentry *dnsentry; 391 if (qp) 392 *qp = NULL; 393 if (!find_in_dns_cache(name, &dnsentry)) { 394 if (get_absolute_time() - dnsentry->absolute_time > DNS_TIMEOUT) 395 goto timeout; 396 memcpy(addr, &dnsentry->addr, sizeof(struct lookup_result)); 397 fn(data, 0); 398 return 0; 399 } 400 timeout: 401 return find_host_no_cache(name, addr, qp, fn, data); 402 } 403 404 void 405 kill_dns_request(void **qp) 406 { 407 struct dnsquery *q = *qp; 408 q->fn = NULL; 409 q->addr = NULL; 410 *qp = NULL; 411 } 412 413 #if MAX_ADDRESSES > 1 414 void 415 dns_set_priority(char *name, struct host_address *address, int prefer) 416 { 417 int i; 418 struct dnsentry *dnsentry; 419 if (find_in_dns_cache(name, &dnsentry)) 420 return; 421 for (i = 0; i < dnsentry->addr.n; i++) 422 if (!memcmp_host_address(&dnsentry->addr.a[i], address)) 423 goto found_it; 424 return; 425 found_it: 426 if (prefer) { 427 memmove(&dnsentry->addr.a[1], &dnsentry->addr.a[0], 428 i * sizeof(struct host_address)); 429 memcpy(&dnsentry->addr.a[0], address, 430 sizeof(struct host_address)); 431 } else { 432 memmove(&dnsentry->addr.a[i], &dnsentry->addr.a[i + 1], 433 (dnsentry->addr.n - i - 1) 434 * sizeof(struct host_address)); 435 memcpy(&dnsentry->addr.a[dnsentry->addr.n - 1], address, 436 sizeof(struct host_address)); 437 } 438 } 439 #endif 440 441 void 442 dns_clear_host(char *name) 443 { 444 struct dnsentry *dnsentry; 445 if (find_in_dns_cache(name, &dnsentry)) 446 return; 447 free_dns_entry(dnsentry); 448 } 449 450 unsigned long 451 dns_info(int type) 452 { 453 if (type == CI_FILES) 454 return list_size(&dns_cache); 455 456 die("dns_info()\n"); 457 /* NOTREACHED */ 458 return 0; 459 } 460 461 static int 462 shrink_dns_cache(int u) 463 { 464 uttime now = get_absolute_time(); 465 struct dnsentry *d = NULL; 466 struct list_head *ld; 467 int f = 0; 468 if (u == SH_FREE_SOMETHING && !list_empty(dns_cache)) { 469 d = list_struct(dns_cache.prev, struct dnsentry); 470 goto delete_last; 471 } 472 foreach (struct dnsentry, d, ld, dns_cache) 473 if (u == SH_FREE_ALL || now - d->absolute_time > DNS_TIMEOUT) { 474 delete_last: 475 ld = d->list_entry.prev; 476 free_dns_entry(d); 477 f = ST_SOMETHING_FREED; 478 } 479 return f | (list_empty(dns_cache) ? ST_CACHE_EMPTY : 0); 480 } 481 482 unsigned char * 483 print_address(struct host_address *a) 484 { 485 #define SCOPE_ID_LEN 11 486 static char buffer[INET6_ADDRSTRLEN + SCOPE_ID_LEN]; 487 union { 488 struct in_addr in; 489 struct in6_addr in6; 490 char pad[16]; 491 } u; 492 memcpy(&u, a->addr, 16); 493 if (!inet_ntop(a->af, &u, buffer, sizeof(buffer) - SCOPE_ID_LEN)) 494 return NULL; 495 if (a->scope_id) { 496 char *end = strchr(buffer, 0); 497 snprintf(end, buffer + sizeof(buffer) - end, "%%%u", 498 a->scope_id); 499 } 500 return (unsigned char *)buffer; 501 } 502 503 int 504 ipv6_full_access(void) 505 { 506 /* 507 * Test if we can access global IPv6 address space. 508 * This doesn't send anything anywhere, it just creates an UDP socket, 509 * connects it and closes it. 510 */ 511 struct sockaddr_in6 sin6; 512 int h, c, rs; 513 if (!support_ipv6) 514 return 0; 515 h = c_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 516 memset(&sin6, 0, sizeof sin6); 517 sin6.sin6_family = AF_INET6; 518 sin6.sin6_port = htons(1024); 519 memcpy( 520 &sin6.sin6_addr.s6_addr, 521 "\052\001\004\060\000\015\000\000\002\314\236\377\376\044\176\032", 522 16); 523 EINTRLOOP(c, connect(h, (struct sockaddr *)(void *)&sin6, sizeof sin6)); 524 EINTRLOOP(rs, close(h)); 525 if (!c) 526 return 1; 527 return 0; 528 } 529 530 void 531 init_dns(void) 532 { 533 register_cache_upcall(shrink_dns_cache, 0, cast_uchar "dns"); 534 }