links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

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 }