blob: deafb78b73cf9daf65b34fb950e79311e3c50c3b [file] [log] [blame]
Tom Marshall91664dd2016-06-17 16:38:12 -07001/*
2 * Copyright (C) 2016 The CyanogenMod Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <fcntl.h>
18#include <netdb.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <ctype.h>
23#include <strings.h>
24#include <sys/file.h>
25#include <sys/mman.h>
26#include <sys/socket.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <unistd.h>
30#include <utime.h>
31#include <pthread.h>
32
33#include <netinet/in6.h>
34#include <arpa/inet.h>
35
36#include "hostent.h"
37#include "resolv_private.h"
38
39#define MAX_ADDRLEN (INET6_ADDRSTRLEN - (1 + 5))
40#define MAX_HOSTLEN MAXHOSTNAMELEN
41
42#define ESTIMATED_LINELEN 32
43#define HCFILE_ALLOC_SIZE 256
44
45/* From sethostent.c */
46#define ALIGNBYTES (sizeof(uintptr_t) - 1)
47#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
48
49/*
50 * Host cache entry for hcfile.c_data.
51 * Offsets are into hcfile.h_data.
52 * Strings are *not* terminated by NULL, but by whitespace (isspace) or '#'.
53 * Use hstr* functions with these.
54 */
55struct hcent
56{
57 uint32_t addr;
58 uint32_t name;
59};
60
61/*
62 * Overall host cache file state.
63 */
64struct hcfile
65{
66 int h_fd;
67 struct stat h_st;
68 char *h_data;
69
70 uint32_t c_alloc;
71 uint32_t c_len;
72 struct hcent *c_data;
73};
74static struct hcfile hcfile;
75static pthread_mutex_t hclock = PTHREAD_MUTEX_INITIALIZER;
76
77static size_t hstrlen(const char *s)
78{
79 const char *p = s;
80 while (*p && *p != '#' && !isspace(*p))
81 ++p;
82 return p - s;
83}
84
85static int hstrcmp(const char *a, const char *b)
86{
87 size_t alen = hstrlen(a);
88 size_t blen = hstrlen(b);
89 int res = strncmp(a, b, MIN(alen, blen));
90 if (res == 0)
91 res = alen - blen;
92 return res;
93}
94
95static char *hstrcpy(char *dest, const char *src)
96{
97 size_t len = hstrlen(src);
98 memcpy(dest, src, len);
99 dest[len] = '\0';
100 return dest;
101}
102
103static char *hstrdup(const char *s)
104{
105 size_t len = hstrlen(s);
106 char *dest = (char *)malloc(len + 1);
107 if (!dest)
108 return NULL;
109 memcpy(dest, s, len);
110 dest[len] = '\0';
111 return dest;
112}
113
114static int cmp_hcent_name(const void *a, const void *b)
115{
116 struct hcent *ea = (struct hcent *)a;
117 const char *na = hcfile.h_data + ea->name;
118 struct hcent *eb = (struct hcent *)b;
119 const char *nb = hcfile.h_data + eb->name;
120
121 return hstrcmp(na, nb);
122}
123
124static struct hcent *_hcfindname(const char *name)
125{
126 size_t first, last, mid;
127 struct hcent *cur = NULL;
128 int cmp;
129
130 if (hcfile.c_len == 0)
131 return NULL;
132
133 first = 0;
134 last = hcfile.c_len - 1;
135 mid = (first + last) / 2;
136 while (first <= last) {
137 cur = hcfile.c_data + mid;
138 cmp = hstrcmp(hcfile.h_data + cur->name, name);
139 if (cmp == 0)
140 goto found;
141 if (cmp < 0)
142 first = mid + 1;
143 else {
144 if (mid > 0)
145 last = mid - 1;
146 else
147 return NULL;
148 }
149 mid = (first + last) / 2;
150 }
151 return NULL;
152
153found:
154 while (cur > hcfile.c_data) {
155 struct hcent *prev = cur - 1;
156 cmp = cmp_hcent_name(cur, prev);
157 if (cmp)
158 break;
159 cur = prev;
160 }
161
162 return cur;
163}
164
165/*
166 * Find next name on line, if any.
167 *
168 * Assumes that line is terminated by LF.
169 */
170static const char *_hcnextname(const char *name)
171{
172 while (!isspace(*name)) {
173 if (*name == '#')
174 return NULL;
175 ++name;
176 }
177 while (isspace(*name)) {
178 if (*name == '\n')
179 return NULL;
180 ++name;
181 }
182 if (*name == '#')
183 return NULL;
184 return name;
185}
186
187static int _hcfilemmap(void)
188{
189 struct stat st;
190 int h_fd;
191 char *h_addr;
192 const char *p, *pend;
193 uint32_t c_alloc;
194
195 h_fd = open(_PATH_HOSTS, O_RDONLY);
196 if (h_fd < 0)
197 return -1;
198 if (flock(h_fd, LOCK_EX) != 0) {
199 close(h_fd);
200 return -1;
201 }
202
203 if (hcfile.h_data) {
204 memset(&st, 0, sizeof(st));
205 if (fstat(h_fd, &st) == 0) {
206 if (st.st_size == hcfile.h_st.st_size &&
207 st.st_mtime == hcfile.h_st.st_mtime) {
208 flock(h_fd, LOCK_UN);
209 close(h_fd);
210 return 0;
211 }
212 }
213 free(hcfile.c_data);
214 munmap(hcfile.h_data, hcfile.h_st.st_size);
215 close(hcfile.h_fd);
216 memset(&hcfile, 0, sizeof(struct hcfile));
217 }
218
219 if (fstat(h_fd, &st) != 0) {
220 flock(h_fd, LOCK_UN);
221 close(h_fd);
222 return -1;
223 }
224 h_addr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, h_fd, 0);
225 if (h_addr == MAP_FAILED) {
226 flock(h_fd, LOCK_UN);
227 close(h_fd);
228 return -1;
229 }
230
231 hcfile.h_fd = h_fd;
232 hcfile.h_st = st;
233 hcfile.h_data = h_addr;
234
235 c_alloc = 0;
236 /*
237 * Do an initial allocation if the file is "large". Estimate
238 * 32 bytes per line and define "large" as more than half of
239 * the alloc growth size (256 entries).
240 */
241 if (st.st_size >= ESTIMATED_LINELEN * HCFILE_ALLOC_SIZE / 2) {
242 c_alloc = st.st_size / ESTIMATED_LINELEN;
243 hcfile.c_data = malloc(c_alloc * sizeof(struct hcent));
244 if (!hcfile.c_data) {
245 goto oom;
246 }
247 }
248
249 p = (const char *)h_addr;
250 pend = p + st.st_size;
251 while (p < pend) {
252 const char *eol, *addr, *name;
253 size_t len;
254 addr = p;
255 eol = memchr(p, '\n', pend - p);
256 if (!eol)
257 break;
258 p = eol + 1;
259 if (*addr == '#' || *addr == '\n')
260 continue;
261 len = hstrlen(addr);
262 if (len > MAX_ADDRLEN)
263 continue;
264 name = addr + len;
265 while (name < eol && isspace(*name))
266 ++name;
267 while (name < eol) {
268 len = hstrlen(name);
269 if (len == 0)
270 break;
271 if (len < MAX_HOSTLEN) {
272 struct hcent *ent;
273 if (c_alloc <= hcfile.c_len) {
274 struct hcent *c_data;
275 c_alloc += HCFILE_ALLOC_SIZE;
276 c_data = realloc(hcfile.c_data, c_alloc * sizeof(struct hcent));
277 if (!c_data) {
278 goto oom;
279 }
280 hcfile.c_data = c_data;
281 }
282 ent = hcfile.c_data + hcfile.c_len;
283 ent->addr = addr - h_addr;
284 ent->name = name - h_addr;
285 ++hcfile.c_len;
286 }
287 name += len;
288 while (name < eol && isspace(*name))
289 ++name;
290 }
291 }
292
293 qsort(hcfile.c_data, hcfile.c_len,
294 sizeof(struct hcent), cmp_hcent_name);
295
296 flock(h_fd, LOCK_UN);
297
298 return 0;
299
300oom:
301 free(hcfile.c_data);
302 munmap(hcfile.h_data, hcfile.h_st.st_size);
303 flock(hcfile.h_fd, LOCK_UN);
304 close(hcfile.h_fd);
305 memset(&hcfile, 0, sizeof(struct hcfile));
306 return -1;
307}
308
309/*
310 * Caching version of getaddrinfo.
311 *
312 * If we find the requested host name in the cache, use getaddrinfo to
313 * populate the result for each address we find.
314 *
315 * Note glibc and bionic differ in the handling of ai_canonname. POSIX
316 * says that ai_canonname is only populated in the first result entry.
317 * glibc does this. bionic populates ai_canonname in all result entries.
318 * We choose the POSIX/glibc way here.
319 */
320int hc_getaddrinfo(const char *host, const char *service,
321 const struct addrinfo *hints,
322 struct addrinfo **result)
323{
324 int ret = 0;
325 struct hcent *ent, *cur;
326 struct addrinfo *ai;
327 struct addrinfo rhints;
328 struct addrinfo *last;
329 int canonname = 0;
330 int cmp;
331
332 if (getenv("ANDROID_HOSTS_CACHE_DISABLE") != NULL)
333 return EAI_SYSTEM;
334
335 /* Avoid needless work and recursion */
336 if (hints && (hints->ai_flags & AI_NUMERICHOST))
337 return EAI_SYSTEM;
338 if (!host)
339 return EAI_SYSTEM;
340
341 pthread_mutex_lock(&hclock);
342
343 if (_hcfilemmap() != 0) {
344 ret = EAI_SYSTEM;
345 goto out;
346 }
347 ent = _hcfindname(host);
348 if (!ent) {
349 ret = EAI_NONAME;
350 goto out;
351 }
352
353 if (hints) {
354 canonname = (hints->ai_flags & AI_CANONNAME);
355 memcpy(&rhints, hints, sizeof(rhints));
356 rhints.ai_flags &= ~AI_CANONNAME;
357 }
358 else {
359 memset(&rhints, 0, sizeof(rhints));
360 }
361 rhints.ai_flags |= AI_NUMERICHOST;
362
363 last = NULL;
364 cur = ent;
365 do {
366 char addrstr[MAX_ADDRLEN];
367 struct addrinfo *res;
368
369 hstrcpy(addrstr, hcfile.h_data + cur->addr);
370
371 if (getaddrinfo(addrstr, service, &rhints, &res) == 0) {
372 if (!last)
373 (*result)->ai_next = res;
374 else
375 last->ai_next = res;
376 last = res;
377 while (last->ai_next)
378 last = last->ai_next;
379 }
380
381 if(cur + 1 >= hcfile.c_data + hcfile.c_len)
382 break;
383 cmp = cmp_hcent_name(cur, cur + 1);
384 cur = cur + 1;
385 }
386 while (!cmp);
387
388 if (last == NULL) {
389 /* This check is equivalent to (*result)->ai_next == NULL */
390 ret = EAI_NODATA;
391 goto out;
392 }
393
394 if (canonname) {
395 ai = (*result)->ai_next;
396 free(ai->ai_canonname);
397 ai->ai_canonname = hstrdup(hcfile.h_data + ent->name);
398 }
399
400out:
401 pthread_mutex_unlock(&hclock);
402 return ret;
403}
404
405/*
406 * Caching version of gethtbyname.
407 *
408 * Note glibc and bionic differ in the handling of aliases. glibc returns
409 * all aliases for all entries, regardless of whether they match h_addrtype.
410 * bionic returns only the aliases for the first hosts entry. We return all
411 * aliases for all IPv4 entries.
412 *
413 * Additionally, if an alias is IPv6 and the primary name for an alias also
414 * has an IPv4 entry, glibc will return the IPv4 address(es), but bionic
415 * will not. Neither do we.
416 */
417int hc_gethtbyname(const char *host, int af, struct getnamaddr *info)
418{
419 int ret = NETDB_SUCCESS;
420 struct hcent *ent, *cur;
421 int cmp;
422 size_t addrlen;
423 unsigned int naliases = 0;
424 char *aliases[MAXALIASES];
425 unsigned int naddrs = 0;
426 char *addr_ptrs[MAXADDRS];
427 unsigned int n;
428
429 if (getenv("ANDROID_HOSTS_CACHE_DISABLE") != NULL)
430 return NETDB_INTERNAL;
431
432 switch (af) {
433 case AF_INET: addrlen = NS_INADDRSZ; break;
434 case AF_INET6: addrlen = NS_IN6ADDRSZ; break;
435 default:
436 return NETDB_INTERNAL;
437 }
438
439 pthread_mutex_lock(&hclock);
440
441 if (_hcfilemmap() != 0) {
442 ret = NETDB_INTERNAL;
443 goto out;
444 }
445
446 ent = _hcfindname(host);
447 if (!ent) {
448 ret = HOST_NOT_FOUND;
449 goto out;
450 }
451
452 cur = ent;
453 do {
454 char addr[16];
455 char addrstr[MAX_ADDRLEN];
456 char namestr[MAX_HOSTLEN];
457 const char *name;
458
459 hstrcpy(addrstr, hcfile.h_data + cur->addr);
460 if (inet_pton(af, addrstr, &addr) == 1) {
461 char *aligned;
462 /* First match is considered the official hostname */
463 if (naddrs == 0) {
464 hstrcpy(namestr, hcfile.h_data + cur->name);
465 HENT_SCOPY(info->hp->h_name, namestr, info->buf, info->buflen);
466 }
467 for (name = hcfile.h_data + cur->name; name; name = _hcnextname(name)) {
468 if (!hstrcmp(name, host))
469 continue;
470 hstrcpy(namestr, name);
471 HENT_SCOPY(aliases[naliases], namestr, info->buf, info->buflen);
472 ++naliases;
473 if (naliases >= MAXALIASES)
474 goto nospc;
475 }
476 aligned = (char *)ALIGN(info->buf);
477 if (info->buf != aligned) {
478 if ((ptrdiff_t)info->buflen < (aligned - info->buf))
479 goto nospc;
480 info->buflen -= (aligned - info->buf);
481 info->buf = aligned;
482 }
483 HENT_COPY(addr_ptrs[naddrs], addr, addrlen, info->buf, info->buflen);
484 ++naddrs;
485 if (naddrs >= MAXADDRS)
486 goto nospc;
487 }
488
489 if(cur + 1 >= hcfile.c_data + hcfile.c_len)
490 break;
491 cmp = cmp_hcent_name(cur, cur + 1);
492 cur = cur + 1;
493 }
494 while (!cmp);
495
496 if (naddrs == 0) {
497 ret = HOST_NOT_FOUND;
498 goto out;
499 }
500
501 addr_ptrs[naddrs++] = NULL;
502 aliases[naliases++] = NULL;
503
504 /* hp->h_name already populated */
505 HENT_ARRAY(info->hp->h_aliases, naliases, info->buf, info->buflen);
506 for (n = 0; n < naliases; ++n) {
507 info->hp->h_aliases[n] = aliases[n];
508 }
509 info->hp->h_addrtype = af;
510 info->hp->h_length = addrlen;
511 HENT_ARRAY(info->hp->h_addr_list, naddrs, info->buf, info->buflen);
512 for (n = 0; n < naddrs; ++n) {
513 info->hp->h_addr_list[n] = addr_ptrs[n];
514 }
515
516out:
517 pthread_mutex_unlock(&hclock);
518 *info->he = ret;
519 return ret;
520
521nospc:
522 ret = NETDB_INTERNAL;
523 goto out;
524}