blob: 573fcbe325c325db05587d7f1ae2e3803c740deb [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include "resolv_cache.h"
Mattias Falk23d3e6b2011-04-04 16:12:35 +020030#include <resolv.h>
Lorenzo Colitti616344d2014-11-28 11:47:13 +090031#include <stdarg.h>
32#include <stdio.h>
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080033#include <stdlib.h>
34#include <string.h>
35#include <time.h>
36#include "pthread.h"
37
Mattias Falk3e0c5102011-01-31 12:42:26 +010038#include <errno.h>
Calin Juravle569fb982014-03-04 15:01:29 +000039#include <arpa/nameser.h>
Mattias Falk3a4910c2011-02-14 12:41:11 +010040#include <sys/system_properties.h>
Mattias Falk23d3e6b2011-04-04 16:12:35 +020041#include <net/if.h>
42#include <netdb.h>
43#include <linux/if.h>
44
45#include <arpa/inet.h>
46#include "resolv_private.h"
Szymon Jakubczakea9bf672014-02-14 17:07:23 -050047#include "resolv_netid.h"
Mattias Falkc63e5902011-08-23 14:34:14 +020048#include "res_private.h"
Mattias Falk3e0c5102011-01-31 12:42:26 +010049
Lorenzo Colitti616344d2014-11-28 11:47:13 +090050#include "private/libc_logging.h"
51
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080052/* This code implements a small and *simple* DNS resolver cache.
53 *
Mattias Falk3e0c5102011-01-31 12:42:26 +010054 * It is only used to cache DNS answers for a time defined by the smallest TTL
55 * among the answer records in order to reduce DNS traffic. It is not supposed
56 * to be a full DNS cache, since we plan to implement that in the future in a
57 * dedicated process running on the system.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080058 *
59 * Note that its design is kept simple very intentionally, i.e.:
60 *
61 * - it takes raw DNS query packet data as input, and returns raw DNS
62 * answer packet data as output
63 *
64 * (this means that two similar queries that encode the DNS name
65 * differently will be treated distinctly).
66 *
Mattias Falk3e0c5102011-01-31 12:42:26 +010067 * the smallest TTL value among the answer records are used as the time
68 * to keep an answer in the cache.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080069 *
70 * this is bad, but we absolutely want to avoid parsing the answer packets
71 * (and should be solved by the later full DNS cache process).
72 *
73 * - the implementation is just a (query-data) => (answer-data) hash table
74 * with a trivial least-recently-used expiration policy.
75 *
76 * Doing this keeps the code simple and avoids to deal with a lot of things
77 * that a full DNS cache is expected to do.
78 *
79 * The API is also very simple:
80 *
81 * - the client calls _resolv_cache_get() to obtain a handle to the cache.
82 * this will initialize the cache on first usage. the result can be NULL
83 * if the cache is disabled.
84 *
85 * - the client calls _resolv_cache_lookup() before performing a query
86 *
87 * if the function returns RESOLV_CACHE_FOUND, a copy of the answer data
88 * has been copied into the client-provided answer buffer.
89 *
90 * if the function returns RESOLV_CACHE_NOTFOUND, the client should perform
91 * a request normally, *then* call _resolv_cache_add() to add the received
92 * answer to the cache.
93 *
94 * if the function returns RESOLV_CACHE_UNSUPPORTED, the client should
95 * perform a request normally, and *not* call _resolv_cache_add()
96 *
97 * note that RESOLV_CACHE_UNSUPPORTED is also returned if the answer buffer
98 * is too short to accomodate the cached result.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080099 */
100
101/* the name of an environment variable that will be checked the first time
102 * this code is called if its value is "0", then the resolver cache is
103 * disabled.
104 */
105#define CONFIG_ENV "BIONIC_DNSCACHE"
106
107/* entries older than CONFIG_SECONDS seconds are always discarded.
108 */
109#define CONFIG_SECONDS (60*10) /* 10 minutes */
110
Mattias Falk3a4910c2011-02-14 12:41:11 +0100111/* default number of entries kept in the cache. This value has been
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800112 * determined by browsing through various sites and counting the number
113 * of corresponding requests. Keep in mind that our framework is currently
114 * performing two requests per name lookup (one for IPv4, the other for IPv6)
115 *
116 * www.google.com 4
117 * www.ysearch.com 6
118 * www.amazon.com 8
119 * www.nytimes.com 22
120 * www.espn.com 28
121 * www.msn.com 28
122 * www.lemonde.fr 35
123 *
124 * (determined in 2009-2-17 from Paris, France, results may vary depending
125 * on location)
126 *
127 * most high-level websites use lots of media/ad servers with different names
128 * but these are generally reused when browsing through the site.
129 *
Mattias Falk3a4910c2011-02-14 12:41:11 +0100130 * As such, a value of 64 should be relatively comfortable at the moment.
131 *
Robert Greenwalt52764f52012-01-25 15:16:03 -0800132 * ******************************************
133 * * NOTE - this has changed.
134 * * 1) we've added IPv6 support so each dns query results in 2 responses
135 * * 2) we've made this a system-wide cache, so the cost is less (it's not
136 * * duplicated in each process) and the need is greater (more processes
137 * * making different requests).
138 * * Upping by 2x for IPv6
139 * * Upping by another 5x for the centralized nature
140 * *****************************************
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800141 */
Robert Greenwalt52764f52012-01-25 15:16:03 -0800142#define CONFIG_MAX_ENTRIES 64 * 2 * 5
Mattias Falk3a4910c2011-02-14 12:41:11 +0100143/* name of the system property that can be used to set the cache size */
Mattias Falk3a4910c2011-02-14 12:41:11 +0100144
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800145/****************************************************************************/
146/****************************************************************************/
147/***** *****/
148/***** *****/
149/***** *****/
150/****************************************************************************/
151/****************************************************************************/
152
153/* set to 1 to debug cache operations */
154#define DEBUG 0
155
156/* set to 1 to debug query data */
157#define DEBUG_DATA 0
158
159#if DEBUG
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900160#define __DEBUG__
161#else
162#define __DEBUG__ __attribute__((unused))
163#endif
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800164
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900165#undef XLOG
166
167#define XLOG(...) ({ \
168 if (DEBUG) { \
169 __libc_format_log(ANDROID_LOG_DEBUG,"libc",__VA_ARGS__); \
170 } else { \
171 ((void)0); \
172 } \
173})
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800174
175/** BOUNDED BUFFER FORMATTING
176 **/
177
178/* technical note:
179 *
180 * the following debugging routines are used to append data to a bounded
181 * buffer they take two parameters that are:
182 *
183 * - p : a pointer to the current cursor position in the buffer
184 * this value is initially set to the buffer's address.
185 *
186 * - end : the address of the buffer's limit, i.e. of the first byte
187 * after the buffer. this address should never be touched.
188 *
189 * IMPORTANT: it is assumed that end > buffer_address, i.e.
190 * that the buffer is at least one byte.
191 *
192 * the _bprint_() functions return the new value of 'p' after the data
193 * has been appended, and also ensure the following:
194 *
195 * - the returned value will never be strictly greater than 'end'
196 *
197 * - a return value equal to 'end' means that truncation occured
198 * (in which case, end[-1] will be set to 0)
199 *
200 * - after returning from a _bprint_() function, the content of the buffer
201 * is always 0-terminated, even in the event of truncation.
202 *
203 * these conventions allow you to call _bprint_ functions multiple times and
204 * only check for truncation at the end of the sequence, as in:
205 *
206 * char buff[1000], *p = buff, *end = p + sizeof(buff);
207 *
208 * p = _bprint_c(p, end, '"');
209 * p = _bprint_s(p, end, my_string);
210 * p = _bprint_c(p, end, '"');
211 *
212 * if (p >= end) {
213 * // buffer was too small
214 * }
215 *
216 * printf( "%s", buff );
217 */
218
219/* add a char to a bounded buffer */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900220char*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800221_bprint_c( char* p, char* end, int c )
222{
223 if (p < end) {
224 if (p+1 == end)
225 *p++ = 0;
226 else {
227 *p++ = (char) c;
228 *p = 0;
229 }
230 }
231 return p;
232}
233
234/* add a sequence of bytes to a bounded buffer */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900235char*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800236_bprint_b( char* p, char* end, const char* buf, int len )
237{
238 int avail = end - p;
239
240 if (avail <= 0 || len <= 0)
241 return p;
242
243 if (avail > len)
244 avail = len;
245
246 memcpy( p, buf, avail );
247 p += avail;
248
249 if (p < end)
250 p[0] = 0;
251 else
252 end[-1] = 0;
253
254 return p;
255}
256
257/* add a string to a bounded buffer */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900258char*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800259_bprint_s( char* p, char* end, const char* str )
260{
261 return _bprint_b(p, end, str, strlen(str));
262}
263
264/* add a formatted string to a bounded buffer */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900265char* _bprint( char* p, char* end, const char* format, ... ) __DEBUG__;
266char* _bprint( char* p, char* end, const char* format, ... )
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800267{
268 int avail, n;
269 va_list args;
270
271 avail = end - p;
272
273 if (avail <= 0)
274 return p;
275
276 va_start(args, format);
David 'Digit' Turnerd378c682010-03-08 15:13:04 -0800277 n = vsnprintf( p, avail, format, args);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800278 va_end(args);
279
280 /* certain C libraries return -1 in case of truncation */
281 if (n < 0 || n > avail)
282 n = avail;
283
284 p += n;
285 /* certain C libraries do not zero-terminate in case of truncation */
286 if (p == end)
287 p[-1] = 0;
288
289 return p;
290}
291
292/* add a hex value to a bounded buffer, up to 8 digits */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900293char*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800294_bprint_hex( char* p, char* end, unsigned value, int numDigits )
295{
296 char text[sizeof(unsigned)*2];
297 int nn = 0;
298
299 while (numDigits-- > 0) {
300 text[nn++] = "0123456789abcdef"[(value >> (numDigits*4)) & 15];
301 }
302 return _bprint_b(p, end, text, nn);
303}
304
305/* add the hexadecimal dump of some memory area to a bounded buffer */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900306char*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800307_bprint_hexdump( char* p, char* end, const uint8_t* data, int datalen )
308{
309 int lineSize = 16;
310
311 while (datalen > 0) {
312 int avail = datalen;
313 int nn;
314
315 if (avail > lineSize)
316 avail = lineSize;
317
318 for (nn = 0; nn < avail; nn++) {
319 if (nn > 0)
320 p = _bprint_c(p, end, ' ');
321 p = _bprint_hex(p, end, data[nn], 2);
322 }
323 for ( ; nn < lineSize; nn++ ) {
324 p = _bprint_s(p, end, " ");
325 }
326 p = _bprint_s(p, end, " ");
327
328 for (nn = 0; nn < avail; nn++) {
329 int c = data[nn];
330
331 if (c < 32 || c > 127)
332 c = '.';
333
334 p = _bprint_c(p, end, c);
335 }
336 p = _bprint_c(p, end, '\n');
337
338 data += avail;
339 datalen -= avail;
340 }
341 return p;
342}
343
344/* dump the content of a query of packet to the log */
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900345void XLOG_BYTES( const void* base, int len ) __DEBUG__;
346void XLOG_BYTES( const void* base, int len )
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800347{
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900348 if (DEBUG_DATA) {
349 char buff[1024];
350 char* p = buff, *end = p + sizeof(buff);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800351
Lorenzo Colitti616344d2014-11-28 11:47:13 +0900352 p = _bprint_hexdump(p, end, base, len);
353 XLOG("%s",buff);
354 }
355} __DEBUG__
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800356
357static time_t
358_time_now( void )
359{
360 struct timeval tv;
361
362 gettimeofday( &tv, NULL );
363 return tv.tv_sec;
364}
365
366/* reminder: the general format of a DNS packet is the following:
367 *
368 * HEADER (12 bytes)
369 * QUESTION (variable)
370 * ANSWER (variable)
371 * AUTHORITY (variable)
372 * ADDITIONNAL (variable)
373 *
374 * the HEADER is made of:
375 *
376 * ID : 16 : 16-bit unique query identification field
377 *
378 * QR : 1 : set to 0 for queries, and 1 for responses
379 * Opcode : 4 : set to 0 for queries
380 * AA : 1 : set to 0 for queries
381 * TC : 1 : truncation flag, will be set to 0 in queries
382 * RD : 1 : recursion desired
383 *
384 * RA : 1 : recursion available (0 in queries)
385 * Z : 3 : three reserved zero bits
386 * RCODE : 4 : response code (always 0=NOERROR in queries)
387 *
388 * QDCount: 16 : question count
389 * ANCount: 16 : Answer count (0 in queries)
390 * NSCount: 16: Authority Record count (0 in queries)
391 * ARCount: 16: Additionnal Record count (0 in queries)
392 *
393 * the QUESTION is made of QDCount Question Record (QRs)
394 * the ANSWER is made of ANCount RRs
395 * the AUTHORITY is made of NSCount RRs
396 * the ADDITIONNAL is made of ARCount RRs
397 *
398 * Each Question Record (QR) is made of:
399 *
400 * QNAME : variable : Query DNS NAME
401 * TYPE : 16 : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255)
402 * CLASS : 16 : class of query (IN=1)
403 *
404 * Each Resource Record (RR) is made of:
405 *
406 * NAME : variable : DNS NAME
407 * TYPE : 16 : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255)
408 * CLASS : 16 : class of query (IN=1)
409 * TTL : 32 : seconds to cache this RR (0=none)
410 * RDLENGTH: 16 : size of RDDATA in bytes
411 * RDDATA : variable : RR data (depends on TYPE)
412 *
413 * Each QNAME contains a domain name encoded as a sequence of 'labels'
414 * terminated by a zero. Each label has the following format:
415 *
416 * LEN : 8 : lenght of label (MUST be < 64)
417 * NAME : 8*LEN : label length (must exclude dots)
418 *
419 * A value of 0 in the encoding is interpreted as the 'root' domain and
420 * terminates the encoding. So 'www.android.com' will be encoded as:
421 *
422 * <3>www<7>android<3>com<0>
423 *
424 * Where <n> represents the byte with value 'n'
425 *
426 * Each NAME reflects the QNAME of the question, but has a slightly more
427 * complex encoding in order to provide message compression. This is achieved
428 * by using a 2-byte pointer, with format:
429 *
430 * TYPE : 2 : 0b11 to indicate a pointer, 0b01 and 0b10 are reserved
431 * OFFSET : 14 : offset to another part of the DNS packet
432 *
433 * The offset is relative to the start of the DNS packet and must point
434 * A pointer terminates the encoding.
435 *
436 * The NAME can be encoded in one of the following formats:
437 *
438 * - a sequence of simple labels terminated by 0 (like QNAMEs)
439 * - a single pointer
440 * - a sequence of simple labels terminated by a pointer
441 *
442 * A pointer shall always point to either a pointer of a sequence of
443 * labels (which can themselves be terminated by either a 0 or a pointer)
444 *
445 * The expanded length of a given domain name should not exceed 255 bytes.
446 *
447 * NOTE: we don't parse the answer packets, so don't need to deal with NAME
448 * records, only QNAMEs.
449 */
450
451#define DNS_HEADER_SIZE 12
452
453#define DNS_TYPE_A "\00\01" /* big-endian decimal 1 */
454#define DNS_TYPE_PTR "\00\014" /* big-endian decimal 12 */
455#define DNS_TYPE_MX "\00\017" /* big-endian decimal 15 */
456#define DNS_TYPE_AAAA "\00\034" /* big-endian decimal 28 */
457#define DNS_TYPE_ALL "\00\0377" /* big-endian decimal 255 */
458
459#define DNS_CLASS_IN "\00\01" /* big-endian decimal 1 */
460
461typedef struct {
462 const uint8_t* base;
463 const uint8_t* end;
464 const uint8_t* cursor;
465} DnsPacket;
466
467static void
468_dnsPacket_init( DnsPacket* packet, const uint8_t* buff, int bufflen )
469{
470 packet->base = buff;
471 packet->end = buff + bufflen;
472 packet->cursor = buff;
473}
474
475static void
476_dnsPacket_rewind( DnsPacket* packet )
477{
478 packet->cursor = packet->base;
479}
480
481static void
482_dnsPacket_skip( DnsPacket* packet, int count )
483{
484 const uint8_t* p = packet->cursor + count;
485
486 if (p > packet->end)
487 p = packet->end;
488
489 packet->cursor = p;
490}
491
492static int
493_dnsPacket_readInt16( DnsPacket* packet )
494{
495 const uint8_t* p = packet->cursor;
496
497 if (p+2 > packet->end)
498 return -1;
499
500 packet->cursor = p+2;
501 return (p[0]<< 8) | p[1];
502}
503
504/** QUERY CHECKING
505 **/
506
507/* check bytes in a dns packet. returns 1 on success, 0 on failure.
508 * the cursor is only advanced in the case of success
509 */
510static int
511_dnsPacket_checkBytes( DnsPacket* packet, int numBytes, const void* bytes )
512{
513 const uint8_t* p = packet->cursor;
514
515 if (p + numBytes > packet->end)
516 return 0;
517
518 if (memcmp(p, bytes, numBytes) != 0)
519 return 0;
520
521 packet->cursor = p + numBytes;
522 return 1;
523}
524
525/* parse and skip a given QNAME stored in a query packet,
526 * from the current cursor position. returns 1 on success,
527 * or 0 for malformed data.
528 */
529static int
530_dnsPacket_checkQName( DnsPacket* packet )
531{
532 const uint8_t* p = packet->cursor;
533 const uint8_t* end = packet->end;
534
535 for (;;) {
536 int c;
537
538 if (p >= end)
539 break;
540
541 c = *p++;
542
543 if (c == 0) {
544 packet->cursor = p;
545 return 1;
546 }
547
548 /* we don't expect label compression in QNAMEs */
549 if (c >= 64)
550 break;
551
552 p += c;
553 /* we rely on the bound check at the start
554 * of the loop here */
555 }
556 /* malformed data */
557 XLOG("malformed QNAME");
558 return 0;
559}
560
561/* parse and skip a given QR stored in a packet.
562 * returns 1 on success, and 0 on failure
563 */
564static int
565_dnsPacket_checkQR( DnsPacket* packet )
566{
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800567 if (!_dnsPacket_checkQName(packet))
568 return 0;
569
570 /* TYPE must be one of the things we support */
571 if (!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_A) &&
572 !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_PTR) &&
573 !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_MX) &&
574 !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_AAAA) &&
575 !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_ALL))
576 {
577 XLOG("unsupported TYPE");
578 return 0;
579 }
580 /* CLASS must be IN */
581 if (!_dnsPacket_checkBytes(packet, 2, DNS_CLASS_IN)) {
582 XLOG("unsupported CLASS");
583 return 0;
584 }
585
586 return 1;
587}
588
589/* check the header of a DNS Query packet, return 1 if it is one
590 * type of query we can cache, or 0 otherwise
591 */
592static int
593_dnsPacket_checkQuery( DnsPacket* packet )
594{
595 const uint8_t* p = packet->base;
596 int qdCount, anCount, dnCount, arCount;
597
598 if (p + DNS_HEADER_SIZE > packet->end) {
599 XLOG("query packet too small");
600 return 0;
601 }
602
603 /* QR must be set to 0, opcode must be 0 and AA must be 0 */
604 /* RA, Z, and RCODE must be 0 */
605 if ((p[2] & 0xFC) != 0 || p[3] != 0) {
606 XLOG("query packet flags unsupported");
607 return 0;
608 }
609
610 /* Note that we ignore the TC and RD bits here for the
611 * following reasons:
612 *
613 * - there is no point for a query packet sent to a server
614 * to have the TC bit set, but the implementation might
615 * set the bit in the query buffer for its own needs
616 * between a _resolv_cache_lookup and a
617 * _resolv_cache_add. We should not freak out if this
618 * is the case.
619 *
620 * - we consider that the result from a RD=0 or a RD=1
621 * query might be different, hence that the RD bit
622 * should be used to differentiate cached result.
623 *
624 * this implies that RD is checked when hashing or
625 * comparing query packets, but not TC
626 */
627
628 /* ANCOUNT, DNCOUNT and ARCOUNT must be 0 */
629 qdCount = (p[4] << 8) | p[5];
630 anCount = (p[6] << 8) | p[7];
631 dnCount = (p[8] << 8) | p[9];
632 arCount = (p[10]<< 8) | p[11];
633
634 if (anCount != 0 || dnCount != 0 || arCount != 0) {
635 XLOG("query packet contains non-query records");
636 return 0;
637 }
638
639 if (qdCount == 0) {
640 XLOG("query packet doesn't contain query record");
641 return 0;
642 }
643
644 /* Check QDCOUNT QRs */
645 packet->cursor = p + DNS_HEADER_SIZE;
646
647 for (;qdCount > 0; qdCount--)
648 if (!_dnsPacket_checkQR(packet))
649 return 0;
650
651 return 1;
652}
653
654/** QUERY DEBUGGING
655 **/
656#if DEBUG
657static char*
658_dnsPacket_bprintQName(DnsPacket* packet, char* bp, char* bend)
659{
660 const uint8_t* p = packet->cursor;
661 const uint8_t* end = packet->end;
662 int first = 1;
663
664 for (;;) {
665 int c;
666
667 if (p >= end)
668 break;
669
670 c = *p++;
671
672 if (c == 0) {
673 packet->cursor = p;
674 return bp;
675 }
676
677 /* we don't expect label compression in QNAMEs */
678 if (c >= 64)
679 break;
680
681 if (first)
682 first = 0;
683 else
684 bp = _bprint_c(bp, bend, '.');
685
686 bp = _bprint_b(bp, bend, (const char*)p, c);
687
688 p += c;
689 /* we rely on the bound check at the start
690 * of the loop here */
691 }
692 /* malformed data */
693 bp = _bprint_s(bp, bend, "<MALFORMED>");
694 return bp;
695}
696
697static char*
698_dnsPacket_bprintQR(DnsPacket* packet, char* p, char* end)
699{
700#define QQ(x) { DNS_TYPE_##x, #x }
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700701 static const struct {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800702 const char* typeBytes;
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700703 const char* typeString;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800704 } qTypes[] =
705 {
706 QQ(A), QQ(PTR), QQ(MX), QQ(AAAA), QQ(ALL),
707 { NULL, NULL }
708 };
709 int nn;
710 const char* typeString = NULL;
711
712 /* dump QNAME */
713 p = _dnsPacket_bprintQName(packet, p, end);
714
715 /* dump TYPE */
716 p = _bprint_s(p, end, " (");
717
718 for (nn = 0; qTypes[nn].typeBytes != NULL; nn++) {
719 if (_dnsPacket_checkBytes(packet, 2, qTypes[nn].typeBytes)) {
720 typeString = qTypes[nn].typeString;
721 break;
722 }
723 }
724
725 if (typeString != NULL)
726 p = _bprint_s(p, end, typeString);
727 else {
728 int typeCode = _dnsPacket_readInt16(packet);
729 p = _bprint(p, end, "UNKNOWN-%d", typeCode);
730 }
731
732 p = _bprint_c(p, end, ')');
733
734 /* skip CLASS */
735 _dnsPacket_skip(packet, 2);
736 return p;
737}
738
739/* this function assumes the packet has already been checked */
740static char*
741_dnsPacket_bprintQuery( DnsPacket* packet, char* p, char* end )
742{
743 int qdCount;
744
745 if (packet->base[2] & 0x1) {
746 p = _bprint_s(p, end, "RECURSIVE ");
747 }
748
749 _dnsPacket_skip(packet, 4);
750 qdCount = _dnsPacket_readInt16(packet);
751 _dnsPacket_skip(packet, 6);
752
753 for ( ; qdCount > 0; qdCount-- ) {
754 p = _dnsPacket_bprintQR(packet, p, end);
755 }
756 return p;
757}
758#endif
759
760
761/** QUERY HASHING SUPPORT
762 **
763 ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKET HAS ALREADY
764 ** BEEN SUCCESFULLY CHECKED.
765 **/
766
767/* use 32-bit FNV hash function */
768#define FNV_MULT 16777619U
769#define FNV_BASIS 2166136261U
770
771static unsigned
772_dnsPacket_hashBytes( DnsPacket* packet, int numBytes, unsigned hash )
773{
774 const uint8_t* p = packet->cursor;
775 const uint8_t* end = packet->end;
776
777 while (numBytes > 0 && p < end) {
778 hash = hash*FNV_MULT ^ *p++;
779 }
780 packet->cursor = p;
781 return hash;
782}
783
784
785static unsigned
786_dnsPacket_hashQName( DnsPacket* packet, unsigned hash )
787{
788 const uint8_t* p = packet->cursor;
789 const uint8_t* end = packet->end;
790
791 for (;;) {
792 int c;
793
794 if (p >= end) { /* should not happen */
795 XLOG("%s: INTERNAL_ERROR: read-overflow !!\n", __FUNCTION__);
796 break;
797 }
798
799 c = *p++;
800
801 if (c == 0)
802 break;
803
804 if (c >= 64) {
805 XLOG("%s: INTERNAL_ERROR: malformed domain !!\n", __FUNCTION__);
806 break;
807 }
808 if (p + c >= end) {
809 XLOG("%s: INTERNAL_ERROR: simple label read-overflow !!\n",
810 __FUNCTION__);
811 break;
812 }
813 while (c > 0) {
814 hash = hash*FNV_MULT ^ *p++;
815 c -= 1;
816 }
817 }
818 packet->cursor = p;
819 return hash;
820}
821
822static unsigned
823_dnsPacket_hashQR( DnsPacket* packet, unsigned hash )
824{
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800825 hash = _dnsPacket_hashQName(packet, hash);
826 hash = _dnsPacket_hashBytes(packet, 4, hash); /* TYPE and CLASS */
827 return hash;
828}
829
830static unsigned
831_dnsPacket_hashQuery( DnsPacket* packet )
832{
833 unsigned hash = FNV_BASIS;
834 int count;
835 _dnsPacket_rewind(packet);
836
837 /* we ignore the TC bit for reasons explained in
838 * _dnsPacket_checkQuery().
839 *
840 * however we hash the RD bit to differentiate
841 * between answers for recursive and non-recursive
842 * queries.
843 */
844 hash = hash*FNV_MULT ^ (packet->base[2] & 1);
845
846 /* assume: other flags are 0 */
847 _dnsPacket_skip(packet, 4);
848
849 /* read QDCOUNT */
850 count = _dnsPacket_readInt16(packet);
851
852 /* assume: ANcount, NScount, ARcount are 0 */
853 _dnsPacket_skip(packet, 6);
854
855 /* hash QDCOUNT QRs */
856 for ( ; count > 0; count-- )
857 hash = _dnsPacket_hashQR(packet, hash);
858
859 return hash;
860}
861
862
863/** QUERY COMPARISON
864 **
865 ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKETS HAVE ALREADY
866 ** BEEN SUCCESFULLY CHECKED.
867 **/
868
869static int
870_dnsPacket_isEqualDomainName( DnsPacket* pack1, DnsPacket* pack2 )
871{
872 const uint8_t* p1 = pack1->cursor;
873 const uint8_t* end1 = pack1->end;
874 const uint8_t* p2 = pack2->cursor;
875 const uint8_t* end2 = pack2->end;
876
877 for (;;) {
878 int c1, c2;
879
880 if (p1 >= end1 || p2 >= end2) {
881 XLOG("%s: INTERNAL_ERROR: read-overflow !!\n", __FUNCTION__);
882 break;
883 }
884 c1 = *p1++;
885 c2 = *p2++;
886 if (c1 != c2)
887 break;
888
889 if (c1 == 0) {
890 pack1->cursor = p1;
891 pack2->cursor = p2;
892 return 1;
893 }
894 if (c1 >= 64) {
895 XLOG("%s: INTERNAL_ERROR: malformed domain !!\n", __FUNCTION__);
896 break;
897 }
898 if ((p1+c1 > end1) || (p2+c1 > end2)) {
899 XLOG("%s: INTERNAL_ERROR: simple label read-overflow !!\n",
900 __FUNCTION__);
901 break;
902 }
903 if (memcmp(p1, p2, c1) != 0)
904 break;
905 p1 += c1;
906 p2 += c1;
907 /* we rely on the bound checks at the start of the loop */
908 }
909 /* not the same, or one is malformed */
910 XLOG("different DN");
911 return 0;
912}
913
914static int
915_dnsPacket_isEqualBytes( DnsPacket* pack1, DnsPacket* pack2, int numBytes )
916{
917 const uint8_t* p1 = pack1->cursor;
918 const uint8_t* p2 = pack2->cursor;
919
920 if ( p1 + numBytes > pack1->end || p2 + numBytes > pack2->end )
921 return 0;
922
923 if ( memcmp(p1, p2, numBytes) != 0 )
924 return 0;
925
926 pack1->cursor += numBytes;
927 pack2->cursor += numBytes;
928 return 1;
929}
930
931static int
932_dnsPacket_isEqualQR( DnsPacket* pack1, DnsPacket* pack2 )
933{
934 /* compare domain name encoding + TYPE + CLASS */
935 if ( !_dnsPacket_isEqualDomainName(pack1, pack2) ||
936 !_dnsPacket_isEqualBytes(pack1, pack2, 2+2) )
937 return 0;
938
939 return 1;
940}
941
942static int
943_dnsPacket_isEqualQuery( DnsPacket* pack1, DnsPacket* pack2 )
944{
945 int count1, count2;
946
947 /* compare the headers, ignore most fields */
948 _dnsPacket_rewind(pack1);
949 _dnsPacket_rewind(pack2);
950
951 /* compare RD, ignore TC, see comment in _dnsPacket_checkQuery */
952 if ((pack1->base[2] & 1) != (pack2->base[2] & 1)) {
953 XLOG("different RD");
954 return 0;
955 }
956
957 /* assume: other flags are all 0 */
958 _dnsPacket_skip(pack1, 4);
959 _dnsPacket_skip(pack2, 4);
960
961 /* compare QDCOUNT */
962 count1 = _dnsPacket_readInt16(pack1);
963 count2 = _dnsPacket_readInt16(pack2);
964 if (count1 != count2 || count1 < 0) {
965 XLOG("different QDCOUNT");
966 return 0;
967 }
968
969 /* assume: ANcount, NScount and ARcount are all 0 */
970 _dnsPacket_skip(pack1, 6);
971 _dnsPacket_skip(pack2, 6);
972
973 /* compare the QDCOUNT QRs */
974 for ( ; count1 > 0; count1-- ) {
975 if (!_dnsPacket_isEqualQR(pack1, pack2)) {
976 XLOG("different QR");
977 return 0;
978 }
979 }
980 return 1;
981}
982
983/****************************************************************************/
984/****************************************************************************/
985/***** *****/
986/***** *****/
987/***** *****/
988/****************************************************************************/
989/****************************************************************************/
990
991/* cache entry. for simplicity, 'hash' and 'hlink' are inlined in this
992 * structure though they are conceptually part of the hash table.
993 *
994 * similarly, mru_next and mru_prev are part of the global MRU list
995 */
996typedef struct Entry {
997 unsigned int hash; /* hash value */
998 struct Entry* hlink; /* next in collision chain */
999 struct Entry* mru_prev;
1000 struct Entry* mru_next;
1001
1002 const uint8_t* query;
1003 int querylen;
1004 const uint8_t* answer;
1005 int answerlen;
Mattias Falk3e0c5102011-01-31 12:42:26 +01001006 time_t expires; /* time_t when the entry isn't valid any more */
1007 int id; /* for debugging purpose */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001008} Entry;
1009
Mattias Falk3e0c5102011-01-31 12:42:26 +01001010/**
Robert Greenwalt78851f12013-01-07 12:10:06 -08001011 * Find the TTL for a negative DNS result. This is defined as the minimum
1012 * of the SOA records TTL and the MINIMUM-TTL field (RFC-2308).
1013 *
1014 * Return 0 if not found.
1015 */
1016static u_long
1017answer_getNegativeTTL(ns_msg handle) {
1018 int n, nscount;
1019 u_long result = 0;
1020 ns_rr rr;
1021
1022 nscount = ns_msg_count(handle, ns_s_ns);
1023 for (n = 0; n < nscount; n++) {
1024 if ((ns_parserr(&handle, ns_s_ns, n, &rr) == 0) && (ns_rr_type(rr) == ns_t_soa)) {
1025 const u_char *rdata = ns_rr_rdata(rr); // find the data
1026 const u_char *edata = rdata + ns_rr_rdlen(rr); // add the len to find the end
1027 int len;
1028 u_long ttl, rec_result = ns_rr_ttl(rr);
1029
1030 // find the MINIMUM-TTL field from the blob of binary data for this record
1031 // skip the server name
1032 len = dn_skipname(rdata, edata);
1033 if (len == -1) continue; // error skipping
1034 rdata += len;
1035
1036 // skip the admin name
1037 len = dn_skipname(rdata, edata);
1038 if (len == -1) continue; // error skipping
1039 rdata += len;
1040
1041 if (edata - rdata != 5*NS_INT32SZ) continue;
1042 // skip: serial number + refresh interval + retry interval + expiry
1043 rdata += NS_INT32SZ * 4;
1044 // finally read the MINIMUM TTL
1045 ttl = ns_get32(rdata);
1046 if (ttl < rec_result) {
1047 rec_result = ttl;
1048 }
1049 // Now that the record is read successfully, apply the new min TTL
1050 if (n == 0 || rec_result < result) {
1051 result = rec_result;
1052 }
1053 }
1054 }
1055 return result;
1056}
1057
1058/**
1059 * Parse the answer records and find the appropriate
1060 * smallest TTL among the records. This might be from
1061 * the answer records if found or from the SOA record
1062 * if it's a negative result.
Mattias Falk3e0c5102011-01-31 12:42:26 +01001063 *
1064 * The returned TTL is the number of seconds to
1065 * keep the answer in the cache.
1066 *
1067 * In case of parse error zero (0) is returned which
1068 * indicates that the answer shall not be cached.
1069 */
1070static u_long
1071answer_getTTL(const void* answer, int answerlen)
1072{
1073 ns_msg handle;
1074 int ancount, n;
1075 u_long result, ttl;
1076 ns_rr rr;
1077
1078 result = 0;
1079 if (ns_initparse(answer, answerlen, &handle) >= 0) {
1080 // get number of answer records
1081 ancount = ns_msg_count(handle, ns_s_an);
Robert Greenwalt78851f12013-01-07 12:10:06 -08001082
1083 if (ancount == 0) {
1084 // a response with no answers? Cache this negative result.
1085 result = answer_getNegativeTTL(handle);
1086 } else {
1087 for (n = 0; n < ancount; n++) {
1088 if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
1089 ttl = ns_rr_ttl(rr);
1090 if (n == 0 || ttl < result) {
1091 result = ttl;
1092 }
1093 } else {
1094 XLOG("ns_parserr failed ancount no = %d. errno = %s\n", n, strerror(errno));
Mattias Falk3e0c5102011-01-31 12:42:26 +01001095 }
Mattias Falk3e0c5102011-01-31 12:42:26 +01001096 }
1097 }
1098 } else {
1099 XLOG("ns_parserr failed. %s\n", strerror(errno));
1100 }
1101
Patrick Tjina6a09492015-01-20 16:02:04 -08001102 XLOG("TTL = %lu\n", result);
Mattias Falk3e0c5102011-01-31 12:42:26 +01001103
1104 return result;
1105}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001106
1107static void
1108entry_free( Entry* e )
1109{
1110 /* everything is allocated in a single memory block */
1111 if (e) {
1112 free(e);
1113 }
1114}
1115
1116static __inline__ void
1117entry_mru_remove( Entry* e )
1118{
1119 e->mru_prev->mru_next = e->mru_next;
1120 e->mru_next->mru_prev = e->mru_prev;
1121}
1122
1123static __inline__ void
1124entry_mru_add( Entry* e, Entry* list )
1125{
1126 Entry* first = list->mru_next;
1127
1128 e->mru_next = first;
1129 e->mru_prev = list;
1130
1131 list->mru_next = e;
1132 first->mru_prev = e;
1133}
1134
1135/* compute the hash of a given entry, this is a hash of most
1136 * data in the query (key) */
1137static unsigned
1138entry_hash( const Entry* e )
1139{
1140 DnsPacket pack[1];
1141
1142 _dnsPacket_init(pack, e->query, e->querylen);
1143 return _dnsPacket_hashQuery(pack);
1144}
1145
1146/* initialize an Entry as a search key, this also checks the input query packet
1147 * returns 1 on success, or 0 in case of unsupported/malformed data */
1148static int
1149entry_init_key( Entry* e, const void* query, int querylen )
1150{
1151 DnsPacket pack[1];
1152
1153 memset(e, 0, sizeof(*e));
1154
1155 e->query = query;
1156 e->querylen = querylen;
1157 e->hash = entry_hash(e);
1158
1159 _dnsPacket_init(pack, query, querylen);
1160
1161 return _dnsPacket_checkQuery(pack);
1162}
1163
1164/* allocate a new entry as a cache node */
1165static Entry*
1166entry_alloc( const Entry* init, const void* answer, int answerlen )
1167{
1168 Entry* e;
1169 int size;
1170
1171 size = sizeof(*e) + init->querylen + answerlen;
1172 e = calloc(size, 1);
1173 if (e == NULL)
1174 return e;
1175
1176 e->hash = init->hash;
1177 e->query = (const uint8_t*)(e+1);
1178 e->querylen = init->querylen;
1179
1180 memcpy( (char*)e->query, init->query, e->querylen );
1181
1182 e->answer = e->query + e->querylen;
1183 e->answerlen = answerlen;
1184
1185 memcpy( (char*)e->answer, answer, e->answerlen );
1186
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001187 return e;
1188}
1189
1190static int
1191entry_equals( const Entry* e1, const Entry* e2 )
1192{
1193 DnsPacket pack1[1], pack2[1];
1194
1195 if (e1->querylen != e2->querylen) {
1196 return 0;
1197 }
1198 _dnsPacket_init(pack1, e1->query, e1->querylen);
1199 _dnsPacket_init(pack2, e2->query, e2->querylen);
1200
1201 return _dnsPacket_isEqualQuery(pack1, pack2);
1202}
1203
1204/****************************************************************************/
1205/****************************************************************************/
1206/***** *****/
1207/***** *****/
1208/***** *****/
1209/****************************************************************************/
1210/****************************************************************************/
1211
1212/* We use a simple hash table with external collision lists
1213 * for simplicity, the hash-table fields 'hash' and 'hlink' are
1214 * inlined in the Entry structure.
1215 */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001216
Mattias Falka59cfcf2011-09-06 15:15:06 +02001217/* Maximum time for a thread to wait for an pending request */
1218#define PENDING_REQUEST_TIMEOUT 20;
1219
1220typedef struct pending_req_info {
1221 unsigned int hash;
1222 pthread_cond_t cond;
1223 struct pending_req_info* next;
1224} PendingReqInfo;
1225
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001226typedef struct resolv_cache {
Mattias Falk3a4910c2011-02-14 12:41:11 +01001227 int max_entries;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001228 int num_entries;
1229 Entry mru_list;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001230 int last_id;
Mattias Falk3a4910c2011-02-14 12:41:11 +01001231 Entry* entries;
Mattias Falka59cfcf2011-09-06 15:15:06 +02001232 PendingReqInfo pending_requests;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001233} Cache;
1234
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001235struct resolv_cache_info {
1236 unsigned netid;
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001237 Cache* cache;
1238 struct resolv_cache_info* next;
1239 char* nameservers[MAXNS +1];
1240 struct addrinfo* nsaddrinfo[MAXNS + 1];
Mattias Falkc63e5902011-08-23 14:34:14 +02001241 char defdname[256];
1242 int dnsrch_offset[MAXDNSRCH+1]; // offsets into defdname
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001243};
Mattias Falkc63e5902011-08-23 14:34:14 +02001244
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001245#define HTABLE_VALID(x) ((x) != NULL && (x) != HTABLE_DELETED)
1246
Paul Jensen41d9a502014-04-08 15:43:41 -04001247static pthread_once_t _res_cache_once = PTHREAD_ONCE_INIT;
1248static void _res_cache_init(void);
1249
1250// lock protecting everything in the _resolve_cache_info structs (next ptr, etc)
1251static pthread_mutex_t _res_cache_list_lock;
1252
1253/* gets cache associated with a network, or NULL if none exists */
1254static struct resolv_cache* _find_named_cache_locked(unsigned netid);
1255
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001256static void
Mattias Falka59cfcf2011-09-06 15:15:06 +02001257_cache_flush_pending_requests_locked( struct resolv_cache* cache )
1258{
1259 struct pending_req_info *ri, *tmp;
1260 if (cache) {
1261 ri = cache->pending_requests.next;
1262
1263 while (ri) {
1264 tmp = ri;
1265 ri = ri->next;
1266 pthread_cond_broadcast(&tmp->cond);
1267
1268 pthread_cond_destroy(&tmp->cond);
1269 free(tmp);
1270 }
1271
1272 cache->pending_requests.next = NULL;
1273 }
1274}
1275
Paul Jensen41d9a502014-04-08 15:43:41 -04001276/* Return 0 if no pending request is found matching the key.
1277 * If a matching request is found the calling thread will wait until
1278 * the matching request completes, then update *cache and return 1. */
Mattias Falka59cfcf2011-09-06 15:15:06 +02001279static int
Paul Jensen41d9a502014-04-08 15:43:41 -04001280_cache_check_pending_request_locked( struct resolv_cache** cache, Entry* key, unsigned netid )
Mattias Falka59cfcf2011-09-06 15:15:06 +02001281{
1282 struct pending_req_info *ri, *prev;
1283 int exist = 0;
1284
Paul Jensen41d9a502014-04-08 15:43:41 -04001285 if (*cache && key) {
1286 ri = (*cache)->pending_requests.next;
1287 prev = &(*cache)->pending_requests;
Mattias Falka59cfcf2011-09-06 15:15:06 +02001288 while (ri) {
1289 if (ri->hash == key->hash) {
1290 exist = 1;
1291 break;
1292 }
1293 prev = ri;
1294 ri = ri->next;
1295 }
1296
1297 if (!exist) {
1298 ri = calloc(1, sizeof(struct pending_req_info));
1299 if (ri) {
1300 ri->hash = key->hash;
1301 pthread_cond_init(&ri->cond, NULL);
1302 prev->next = ri;
1303 }
1304 } else {
1305 struct timespec ts = {0,0};
Mattias Falkc63e5902011-08-23 14:34:14 +02001306 XLOG("Waiting for previous request");
Mattias Falka59cfcf2011-09-06 15:15:06 +02001307 ts.tv_sec = _time_now() + PENDING_REQUEST_TIMEOUT;
Paul Jensen41d9a502014-04-08 15:43:41 -04001308 pthread_cond_timedwait(&ri->cond, &_res_cache_list_lock, &ts);
1309 /* Must update *cache as it could have been deleted. */
1310 *cache = _find_named_cache_locked(netid);
Mattias Falka59cfcf2011-09-06 15:15:06 +02001311 }
1312 }
1313
1314 return exist;
1315}
1316
1317/* notify any waiting thread that waiting on a request
1318 * matching the key has been added to the cache */
1319static void
1320_cache_notify_waiting_tid_locked( struct resolv_cache* cache, Entry* key )
1321{
1322 struct pending_req_info *ri, *prev;
1323
1324 if (cache && key) {
1325 ri = cache->pending_requests.next;
1326 prev = &cache->pending_requests;
1327 while (ri) {
1328 if (ri->hash == key->hash) {
1329 pthread_cond_broadcast(&ri->cond);
1330 break;
1331 }
1332 prev = ri;
1333 ri = ri->next;
1334 }
1335
1336 // remove item from list and destroy
1337 if (ri) {
1338 prev->next = ri->next;
1339 pthread_cond_destroy(&ri->cond);
1340 free(ri);
1341 }
1342 }
1343}
1344
1345/* notify the cache that the query failed */
1346void
Paul Jensen41d9a502014-04-08 15:43:41 -04001347_resolv_cache_query_failed( unsigned netid,
Mattias Falka59cfcf2011-09-06 15:15:06 +02001348 const void* query,
1349 int querylen)
1350{
1351 Entry key[1];
Paul Jensen41d9a502014-04-08 15:43:41 -04001352 Cache* cache;
Mattias Falka59cfcf2011-09-06 15:15:06 +02001353
Paul Jensen41d9a502014-04-08 15:43:41 -04001354 if (!entry_init_key(key, query, querylen))
1355 return;
1356
1357 pthread_mutex_lock(&_res_cache_list_lock);
1358
1359 cache = _find_named_cache_locked(netid);
1360
1361 if (cache) {
Mattias Falka59cfcf2011-09-06 15:15:06 +02001362 _cache_notify_waiting_tid_locked(cache, key);
Mattias Falka59cfcf2011-09-06 15:15:06 +02001363 }
Paul Jensen41d9a502014-04-08 15:43:41 -04001364
1365 pthread_mutex_unlock(&_res_cache_list_lock);
Mattias Falka59cfcf2011-09-06 15:15:06 +02001366}
1367
1368static void
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001369_cache_flush_locked( Cache* cache )
1370{
1371 int nn;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001372
Mattias Falk3a4910c2011-02-14 12:41:11 +01001373 for (nn = 0; nn < cache->max_entries; nn++)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001374 {
Mattias Falk3a4910c2011-02-14 12:41:11 +01001375 Entry** pnode = (Entry**) &cache->entries[nn];
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001376
1377 while (*pnode != NULL) {
1378 Entry* node = *pnode;
1379 *pnode = node->hlink;
1380 entry_free(node);
1381 }
1382 }
1383
Mattias Falka59cfcf2011-09-06 15:15:06 +02001384 // flush pending request
1385 _cache_flush_pending_requests_locked(cache);
1386
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001387 cache->mru_list.mru_next = cache->mru_list.mru_prev = &cache->mru_list;
1388 cache->num_entries = 0;
1389 cache->last_id = 0;
1390
1391 XLOG("*************************\n"
1392 "*** DNS CACHE FLUSHED ***\n"
1393 "*************************");
1394}
1395
Mattias Falk3a4910c2011-02-14 12:41:11 +01001396static int
1397_res_cache_get_max_entries( void )
1398{
Elliott Hughes908e8c22014-01-28 14:54:11 -08001399 int cache_size = CONFIG_MAX_ENTRIES;
Mattias Falk3a4910c2011-02-14 12:41:11 +01001400
Robert Greenwalt52764f52012-01-25 15:16:03 -08001401 const char* cache_mode = getenv("ANDROID_DNS_MODE");
Robert Greenwalt52764f52012-01-25 15:16:03 -08001402 if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) {
Elliott Hughes908e8c22014-01-28 14:54:11 -08001403 // Don't use the cache in local mode. This is used by the proxy itself.
1404 cache_size = 0;
Robert Greenwalt52764f52012-01-25 15:16:03 -08001405 }
1406
Elliott Hughes908e8c22014-01-28 14:54:11 -08001407 XLOG("cache size: %d", cache_size);
1408 return cache_size;
Mattias Falk3a4910c2011-02-14 12:41:11 +01001409}
1410
Jim Huang7cc56662010-10-15 02:02:57 +08001411static struct resolv_cache*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001412_resolv_cache_create( void )
1413{
1414 struct resolv_cache* cache;
1415
1416 cache = calloc(sizeof(*cache), 1);
1417 if (cache) {
Mattias Falk3a4910c2011-02-14 12:41:11 +01001418 cache->max_entries = _res_cache_get_max_entries();
1419 cache->entries = calloc(sizeof(*cache->entries), cache->max_entries);
1420 if (cache->entries) {
Mattias Falk3a4910c2011-02-14 12:41:11 +01001421 cache->mru_list.mru_prev = cache->mru_list.mru_next = &cache->mru_list;
1422 XLOG("%s: cache created\n", __FUNCTION__);
1423 } else {
1424 free(cache);
1425 cache = NULL;
1426 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001427 }
1428 return cache;
1429}
1430
1431
1432#if DEBUG
1433static void
1434_dump_query( const uint8_t* query, int querylen )
1435{
1436 char temp[256], *p=temp, *end=p+sizeof(temp);
1437 DnsPacket pack[1];
1438
1439 _dnsPacket_init(pack, query, querylen);
1440 p = _dnsPacket_bprintQuery(pack, p, end);
1441 XLOG("QUERY: %s", temp);
1442}
1443
1444static void
1445_cache_dump_mru( Cache* cache )
1446{
1447 char temp[512], *p=temp, *end=p+sizeof(temp);
1448 Entry* e;
1449
1450 p = _bprint(temp, end, "MRU LIST (%2d): ", cache->num_entries);
1451 for (e = cache->mru_list.mru_next; e != &cache->mru_list; e = e->mru_next)
1452 p = _bprint(p, end, " %d", e->id);
1453
1454 XLOG("%s", temp);
1455}
Mattias Falk3e0c5102011-01-31 12:42:26 +01001456
1457static void
1458_dump_answer(const void* answer, int answerlen)
1459{
1460 res_state statep;
1461 FILE* fp;
1462 char* buf;
1463 int fileLen;
1464
Elliott Hughesc674edb2014-08-26 15:56:54 -07001465 fp = fopen("/data/reslog.txt", "w+e");
Mattias Falk3e0c5102011-01-31 12:42:26 +01001466 if (fp != NULL) {
1467 statep = __res_get_state();
1468
1469 res_pquery(statep, answer, answerlen, fp);
1470
1471 //Get file length
1472 fseek(fp, 0, SEEK_END);
1473 fileLen=ftell(fp);
1474 fseek(fp, 0, SEEK_SET);
1475 buf = (char *)malloc(fileLen+1);
1476 if (buf != NULL) {
1477 //Read file contents into buffer
1478 fread(buf, fileLen, 1, fp);
1479 XLOG("%s\n", buf);
1480 free(buf);
1481 }
1482 fclose(fp);
1483 remove("/data/reslog.txt");
1484 }
1485 else {
Robert Greenwalt78851f12013-01-07 12:10:06 -08001486 errno = 0; // else debug is introducing error signals
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001487 XLOG("%s: can't open file\n", __FUNCTION__);
Mattias Falk3e0c5102011-01-31 12:42:26 +01001488 }
1489}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001490#endif
1491
1492#if DEBUG
1493# define XLOG_QUERY(q,len) _dump_query((q), (len))
Mattias Falk3e0c5102011-01-31 12:42:26 +01001494# define XLOG_ANSWER(a, len) _dump_answer((a), (len))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001495#else
1496# define XLOG_QUERY(q,len) ((void)0)
Mattias Falk3e0c5102011-01-31 12:42:26 +01001497# define XLOG_ANSWER(a,len) ((void)0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001498#endif
1499
1500/* This function tries to find a key within the hash table
1501 * In case of success, it will return a *pointer* to the hashed key.
1502 * In case of failure, it will return a *pointer* to NULL
1503 *
1504 * So, the caller must check '*result' to check for success/failure.
1505 *
1506 * The main idea is that the result can later be used directly in
1507 * calls to _resolv_cache_add or _resolv_cache_remove as the 'lookup'
1508 * parameter. This makes the code simpler and avoids re-searching
1509 * for the key position in the htable.
1510 *
1511 * The result of a lookup_p is only valid until you alter the hash
1512 * table.
1513 */
1514static Entry**
1515_cache_lookup_p( Cache* cache,
1516 Entry* key )
1517{
Mattias Falk3a4910c2011-02-14 12:41:11 +01001518 int index = key->hash % cache->max_entries;
1519 Entry** pnode = (Entry**) &cache->entries[ index ];
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001520
1521 while (*pnode != NULL) {
1522 Entry* node = *pnode;
1523
1524 if (node == NULL)
1525 break;
1526
1527 if (node->hash == key->hash && entry_equals(node, key))
1528 break;
1529
1530 pnode = &node->hlink;
1531 }
Mattias Falkc63e5902011-08-23 14:34:14 +02001532 return pnode;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001533}
1534
1535/* Add a new entry to the hash table. 'lookup' must be the
1536 * result of an immediate previous failed _lookup_p() call
1537 * (i.e. with *lookup == NULL), and 'e' is the pointer to the
1538 * newly created entry
1539 */
1540static void
1541_cache_add_p( Cache* cache,
1542 Entry** lookup,
1543 Entry* e )
1544{
1545 *lookup = e;
1546 e->id = ++cache->last_id;
1547 entry_mru_add(e, &cache->mru_list);
1548 cache->num_entries += 1;
1549
1550 XLOG("%s: entry %d added (count=%d)", __FUNCTION__,
1551 e->id, cache->num_entries);
1552}
1553
1554/* Remove an existing entry from the hash table,
1555 * 'lookup' must be the result of an immediate previous
1556 * and succesful _lookup_p() call.
1557 */
1558static void
1559_cache_remove_p( Cache* cache,
1560 Entry** lookup )
1561{
1562 Entry* e = *lookup;
1563
1564 XLOG("%s: entry %d removed (count=%d)", __FUNCTION__,
1565 e->id, cache->num_entries-1);
1566
1567 entry_mru_remove(e);
1568 *lookup = e->hlink;
1569 entry_free(e);
1570 cache->num_entries -= 1;
1571}
1572
1573/* Remove the oldest entry from the hash table.
1574 */
1575static void
1576_cache_remove_oldest( Cache* cache )
1577{
1578 Entry* oldest = cache->mru_list.mru_prev;
1579 Entry** lookup = _cache_lookup_p(cache, oldest);
1580
1581 if (*lookup == NULL) { /* should not happen */
1582 XLOG("%s: OLDEST NOT IN HTABLE ?", __FUNCTION__);
1583 return;
1584 }
Robert Greenwalt7f84da62011-09-02 07:44:36 -07001585 if (DEBUG) {
1586 XLOG("Cache full - removing oldest");
1587 XLOG_QUERY(oldest->query, oldest->querylen);
1588 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001589 _cache_remove_p(cache, lookup);
1590}
1591
Anders Fredlunddd161822011-05-20 08:12:37 +02001592/* Remove all expired entries from the hash table.
1593 */
1594static void _cache_remove_expired(Cache* cache) {
1595 Entry* e;
1596 time_t now = _time_now();
1597
1598 for (e = cache->mru_list.mru_next; e != &cache->mru_list;) {
1599 // Entry is old, remove
1600 if (now >= e->expires) {
1601 Entry** lookup = _cache_lookup_p(cache, e);
1602 if (*lookup == NULL) { /* should not happen */
1603 XLOG("%s: ENTRY NOT IN HTABLE ?", __FUNCTION__);
1604 return;
1605 }
1606 e = e->mru_next;
1607 _cache_remove_p(cache, lookup);
1608 } else {
1609 e = e->mru_next;
1610 }
1611 }
1612}
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001613
1614ResolvCacheStatus
Paul Jensen41d9a502014-04-08 15:43:41 -04001615_resolv_cache_lookup( unsigned netid,
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001616 const void* query,
1617 int querylen,
1618 void* answer,
1619 int answersize,
1620 int *answerlen )
1621{
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001622 Entry key[1];
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001623 Entry** lookup;
1624 Entry* e;
1625 time_t now;
Paul Jensen41d9a502014-04-08 15:43:41 -04001626 Cache* cache;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001627
1628 ResolvCacheStatus result = RESOLV_CACHE_NOTFOUND;
1629
1630 XLOG("%s: lookup", __FUNCTION__);
1631 XLOG_QUERY(query, querylen);
1632
1633 /* we don't cache malformed queries */
1634 if (!entry_init_key(key, query, querylen)) {
1635 XLOG("%s: unsupported query", __FUNCTION__);
1636 return RESOLV_CACHE_UNSUPPORTED;
1637 }
1638 /* lookup cache */
Paul Jensen41d9a502014-04-08 15:43:41 -04001639 pthread_once(&_res_cache_once, _res_cache_init);
1640 pthread_mutex_lock(&_res_cache_list_lock);
1641
1642 cache = _find_named_cache_locked(netid);
1643 if (cache == NULL) {
1644 result = RESOLV_CACHE_UNSUPPORTED;
1645 goto Exit;
1646 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001647
1648 /* see the description of _lookup_p to understand this.
1649 * the function always return a non-NULL pointer.
1650 */
1651 lookup = _cache_lookup_p(cache, key);
1652 e = *lookup;
1653
1654 if (e == NULL) {
1655 XLOG( "NOT IN CACHE");
Mattias Falka59cfcf2011-09-06 15:15:06 +02001656 // calling thread will wait if an outstanding request is found
1657 // that matching this query
Paul Jensen41d9a502014-04-08 15:43:41 -04001658 if (!_cache_check_pending_request_locked(&cache, key, netid) || cache == NULL) {
Mattias Falka59cfcf2011-09-06 15:15:06 +02001659 goto Exit;
1660 } else {
1661 lookup = _cache_lookup_p(cache, key);
1662 e = *lookup;
1663 if (e == NULL) {
1664 goto Exit;
1665 }
1666 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001667 }
1668
1669 now = _time_now();
1670
1671 /* remove stale entries here */
Mattias Falk3e0c5102011-01-31 12:42:26 +01001672 if (now >= e->expires) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001673 XLOG( " NOT IN CACHE (STALE ENTRY %p DISCARDED)", *lookup );
Robert Greenwalt7f84da62011-09-02 07:44:36 -07001674 XLOG_QUERY(e->query, e->querylen);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001675 _cache_remove_p(cache, lookup);
1676 goto Exit;
1677 }
1678
1679 *answerlen = e->answerlen;
1680 if (e->answerlen > answersize) {
1681 /* NOTE: we return UNSUPPORTED if the answer buffer is too short */
1682 result = RESOLV_CACHE_UNSUPPORTED;
1683 XLOG(" ANSWER TOO LONG");
1684 goto Exit;
1685 }
1686
1687 memcpy( answer, e->answer, e->answerlen );
1688
1689 /* bump up this entry to the top of the MRU list */
1690 if (e != cache->mru_list.mru_next) {
1691 entry_mru_remove( e );
1692 entry_mru_add( e, &cache->mru_list );
1693 }
1694
1695 XLOG( "FOUND IN CACHE entry=%p", e );
1696 result = RESOLV_CACHE_FOUND;
1697
1698Exit:
Paul Jensen41d9a502014-04-08 15:43:41 -04001699 pthread_mutex_unlock(&_res_cache_list_lock);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001700 return result;
1701}
1702
1703
1704void
Paul Jensen41d9a502014-04-08 15:43:41 -04001705_resolv_cache_add( unsigned netid,
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001706 const void* query,
1707 int querylen,
1708 const void* answer,
1709 int answerlen )
1710{
1711 Entry key[1];
1712 Entry* e;
1713 Entry** lookup;
Mattias Falk3e0c5102011-01-31 12:42:26 +01001714 u_long ttl;
Paul Jensen41d9a502014-04-08 15:43:41 -04001715 Cache* cache = NULL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001716
1717 /* don't assume that the query has already been cached
1718 */
1719 if (!entry_init_key( key, query, querylen )) {
1720 XLOG( "%s: passed invalid query ?", __FUNCTION__);
1721 return;
1722 }
1723
Paul Jensen41d9a502014-04-08 15:43:41 -04001724 pthread_mutex_lock(&_res_cache_list_lock);
1725
1726 cache = _find_named_cache_locked(netid);
1727 if (cache == NULL) {
1728 goto Exit;
1729 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001730
1731 XLOG( "%s: query:", __FUNCTION__ );
1732 XLOG_QUERY(query,querylen);
Mattias Falk3e0c5102011-01-31 12:42:26 +01001733 XLOG_ANSWER(answer, answerlen);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001734#if DEBUG_DATA
1735 XLOG( "answer:");
1736 XLOG_BYTES(answer,answerlen);
1737#endif
1738
1739 lookup = _cache_lookup_p(cache, key);
1740 e = *lookup;
1741
1742 if (e != NULL) { /* should not happen */
1743 XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD",
1744 __FUNCTION__, e);
1745 goto Exit;
1746 }
1747
Mattias Falk3a4910c2011-02-14 12:41:11 +01001748 if (cache->num_entries >= cache->max_entries) {
Anders Fredlunddd161822011-05-20 08:12:37 +02001749 _cache_remove_expired(cache);
1750 if (cache->num_entries >= cache->max_entries) {
1751 _cache_remove_oldest(cache);
1752 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001753 /* need to lookup again */
1754 lookup = _cache_lookup_p(cache, key);
1755 e = *lookup;
1756 if (e != NULL) {
1757 XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD",
1758 __FUNCTION__, e);
1759 goto Exit;
1760 }
1761 }
1762
Mattias Falk3e0c5102011-01-31 12:42:26 +01001763 ttl = answer_getTTL(answer, answerlen);
1764 if (ttl > 0) {
1765 e = entry_alloc(key, answer, answerlen);
1766 if (e != NULL) {
1767 e->expires = ttl + _time_now();
1768 _cache_add_p(cache, lookup, e);
1769 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001770 }
1771#if DEBUG
1772 _cache_dump_mru(cache);
1773#endif
1774Exit:
Paul Jensen41d9a502014-04-08 15:43:41 -04001775 if (cache != NULL) {
1776 _cache_notify_waiting_tid_locked(cache, key);
1777 }
1778 pthread_mutex_unlock(&_res_cache_list_lock);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001779}
1780
1781/****************************************************************************/
1782/****************************************************************************/
1783/***** *****/
1784/***** *****/
1785/***** *****/
1786/****************************************************************************/
1787/****************************************************************************/
1788
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001789// Head of the list of caches. Protected by _res_cache_list_lock.
1790static struct resolv_cache_info _res_cache_list;
1791
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001792/* insert resolv_cache_info into the list of resolv_cache_infos */
1793static void _insert_cache_info_locked(struct resolv_cache_info* cache_info);
1794/* creates a resolv_cache_info */
1795static struct resolv_cache_info* _create_cache_info( void );
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001796/* gets a resolv_cache_info associated with a network, or NULL if not found */
1797static struct resolv_cache_info* _find_cache_info_locked(unsigned netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001798/* look up the named cache, and creates one if needed */
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001799static struct resolv_cache* _get_res_cache_for_net_locked(unsigned netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001800/* empty the named cache */
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001801static void _flush_cache_for_net_locked(unsigned netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001802/* empty the nameservers set for the named cache */
1803static void _free_nameservers_locked(struct resolv_cache_info* cache_info);
Mattias Falkc63e5902011-08-23 14:34:14 +02001804/* return 1 if the provided list of name servers differs from the list of name servers
1805 * currently attached to the provided cache_info */
1806static int _resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info,
Sasha Levitskiyfbae9f32013-02-27 15:48:55 -08001807 const char** servers, int numservers);
Chad Brubaker0c9bb492013-05-30 13:37:02 -07001808
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001809static void
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001810_res_cache_init(void)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001811{
1812 const char* env = getenv(CONFIG_ENV);
1813
1814 if (env && atoi(env) == 0) {
1815 /* the cache is disabled */
1816 return;
1817 }
1818
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001819 memset(&_res_cache_list, 0, sizeof(_res_cache_list));
1820 pthread_mutex_init(&_res_cache_list_lock, NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001821}
1822
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001823static struct resolv_cache*
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001824_get_res_cache_for_net_locked(unsigned netid)
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001825{
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001826 struct resolv_cache* cache = _find_named_cache_locked(netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001827 if (!cache) {
1828 struct resolv_cache_info* cache_info = _create_cache_info();
1829 if (cache_info) {
1830 cache = _resolv_cache_create();
1831 if (cache) {
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001832 cache_info->cache = cache;
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001833 cache_info->netid = netid;
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001834 _insert_cache_info_locked(cache_info);
1835 } else {
1836 free(cache_info);
1837 }
1838 }
1839 }
1840 return cache;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001841}
1842
Paul Jensen1544eae2014-08-06 17:34:22 +00001843void
1844_resolv_flush_cache_for_net(unsigned netid)
1845{
1846 pthread_once(&_res_cache_once, _res_cache_init);
1847 pthread_mutex_lock(&_res_cache_list_lock);
1848
1849 _flush_cache_for_net_locked(netid);
1850
1851 pthread_mutex_unlock(&_res_cache_list_lock);
1852}
1853
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001854static void
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001855_flush_cache_for_net_locked(unsigned netid)
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001856{
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001857 struct resolv_cache* cache = _find_named_cache_locked(netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001858 if (cache) {
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001859 _cache_flush_locked(cache);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001860 }
1861}
1862
Paul Jensen41d9a502014-04-08 15:43:41 -04001863void _resolv_delete_cache_for_net(unsigned netid)
1864{
1865 pthread_once(&_res_cache_once, _res_cache_init);
1866 pthread_mutex_lock(&_res_cache_list_lock);
1867
1868 struct resolv_cache_info* prev_cache_info = &_res_cache_list;
1869
1870 while (prev_cache_info->next) {
1871 struct resolv_cache_info* cache_info = prev_cache_info->next;
1872
1873 if (cache_info->netid == netid) {
1874 prev_cache_info->next = cache_info->next;
1875 _cache_flush_locked(cache_info->cache);
1876 free(cache_info->cache->entries);
1877 free(cache_info->cache);
1878 _free_nameservers_locked(cache_info);
1879 free(cache_info);
1880 break;
1881 }
1882
1883 prev_cache_info = prev_cache_info->next;
1884 }
1885
1886 pthread_mutex_unlock(&_res_cache_list_lock);
1887}
1888
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001889static struct resolv_cache_info*
1890_create_cache_info(void)
1891{
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001892 struct resolv_cache_info* cache_info;
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001893
1894 cache_info = calloc(sizeof(*cache_info), 1);
1895 return cache_info;
1896}
1897
1898static void
1899_insert_cache_info_locked(struct resolv_cache_info* cache_info)
1900{
1901 struct resolv_cache_info* last;
1902
1903 for (last = &_res_cache_list; last->next; last = last->next);
1904
1905 last->next = cache_info;
1906
1907}
1908
1909static struct resolv_cache*
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001910_find_named_cache_locked(unsigned netid) {
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001911
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001912 struct resolv_cache_info* info = _find_cache_info_locked(netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001913
1914 if (info != NULL) return info->cache;
1915
1916 return NULL;
1917}
1918
1919static struct resolv_cache_info*
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001920_find_cache_info_locked(unsigned netid)
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001921{
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001922 struct resolv_cache_info* cache_info = _res_cache_list.next;
1923
1924 while (cache_info) {
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001925 if (cache_info->netid == netid) {
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001926 break;
1927 }
1928
1929 cache_info = cache_info->next;
1930 }
1931 return cache_info;
1932}
1933
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001934void
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001935_resolv_set_nameservers_for_net(unsigned netid, const char** servers, int numservers,
Robert Greenwalt6f3222e2012-11-13 11:50:57 -08001936 const char *domains)
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001937{
1938 int i, rt, index;
1939 struct addrinfo hints;
1940 char sbuf[NI_MAXSERV];
Mattias Falkc63e5902011-08-23 14:34:14 +02001941 register char *cp;
1942 int *offset;
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001943
1944 pthread_once(&_res_cache_once, _res_cache_init);
Robert Greenwaltb002a2f2013-01-19 00:40:24 +00001945 pthread_mutex_lock(&_res_cache_list_lock);
Mattias Falkc63e5902011-08-23 14:34:14 +02001946
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001947 // creates the cache if not created
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001948 _get_res_cache_for_net_locked(netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001949
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001950 struct resolv_cache_info* cache_info = _find_cache_info_locked(netid);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001951
Mattias Falkc63e5902011-08-23 14:34:14 +02001952 if (cache_info != NULL &&
1953 !_resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) {
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001954 // free current before adding new
1955 _free_nameservers_locked(cache_info);
1956
1957 memset(&hints, 0, sizeof(hints));
1958 hints.ai_family = PF_UNSPEC;
1959 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1960 hints.ai_flags = AI_NUMERICHOST;
Dan Albertcfd8c452014-10-07 21:08:41 -07001961 snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001962
1963 index = 0;
1964 for (i = 0; i < numservers && i < MAXNS; i++) {
1965 rt = getaddrinfo(servers[i], sbuf, &hints, &cache_info->nsaddrinfo[index]);
1966 if (rt == 0) {
1967 cache_info->nameservers[index] = strdup(servers[i]);
1968 index++;
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001969 XLOG("%s: netid = %u, addr = %s\n", __FUNCTION__, netid, servers[i]);
Mattias Falk23d3e6b2011-04-04 16:12:35 +02001970 } else {
1971 cache_info->nsaddrinfo[index] = NULL;
1972 }
1973 }
Mattias Falkc63e5902011-08-23 14:34:14 +02001974
1975 // code moved from res_init.c, load_domain_search_list
1976 strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname));
1977 if ((cp = strchr(cache_info->defdname, '\n')) != NULL)
1978 *cp = '\0';
1979 cp = cache_info->defdname;
1980 offset = cache_info->dnsrch_offset;
1981 while (offset < cache_info->dnsrch_offset + MAXDNSRCH) {
1982 while (*cp == ' ' || *cp == '\t') /* skip leading white space */
1983 cp++;
1984 if (*cp == '\0') /* stop if nothing more to do */
1985 break;
1986 *offset++ = cp - cache_info->defdname; /* record this search domain */
1987 while (*cp) { /* zero-terminate it */
1988 if (*cp == ' '|| *cp == '\t') {
1989 *cp++ = '\0';
1990 break;
1991 }
1992 cp++;
1993 }
1994 }
1995 *offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
1996
1997 // flush cache since new settings
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05001998 _flush_cache_for_net_locked(netid);
Mattias Falkc63e5902011-08-23 14:34:14 +02001999
Mattias Falk23d3e6b2011-04-04 16:12:35 +02002000 }
Mattias Falkc63e5902011-08-23 14:34:14 +02002001
Mattias Falk23d3e6b2011-04-04 16:12:35 +02002002 pthread_mutex_unlock(&_res_cache_list_lock);
2003}
2004
Mattias Falkc63e5902011-08-23 14:34:14 +02002005static int
2006_resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info,
Sasha Levitskiyfbae9f32013-02-27 15:48:55 -08002007 const char** servers, int numservers)
Mattias Falkc63e5902011-08-23 14:34:14 +02002008{
2009 int i;
2010 char** ns;
Lorenzo Colittibce18c92014-09-08 18:09:43 +09002011 int currentservers;
Mattias Falkc63e5902011-08-23 14:34:14 +02002012 int equal = 1;
2013
Mattias Falkc63e5902011-08-23 14:34:14 +02002014 if (numservers > MAXNS) numservers = MAXNS;
Lorenzo Colittibce18c92014-09-08 18:09:43 +09002015
2016 // Find out how many nameservers we had before.
2017 currentservers = 0;
2018 for (ns = cache_info->nameservers; *ns; ns++)
2019 currentservers++;
2020
2021 if (currentservers != numservers)
2022 return 0;
2023
2024 // Compare each name server against current name servers.
2025 // TODO: this is incorrect if the list of current or previous nameservers
2026 // contains duplicates. This does not really matter because the framework
2027 // filters out duplicates, but we should probably fix it. It's also
2028 // insensitive to the order of the nameservers; we should probably fix that
2029 // too.
Mattias Falkc63e5902011-08-23 14:34:14 +02002030 for (i = 0; i < numservers && equal; i++) {
2031 ns = cache_info->nameservers;
2032 equal = 0;
2033 while(*ns) {
2034 if (strcmp(*ns, servers[i]) == 0) {
2035 equal = 1;
2036 break;
2037 }
2038 ns++;
2039 }
2040 }
2041
2042 return equal;
2043}
2044
Mattias Falk23d3e6b2011-04-04 16:12:35 +02002045static void
2046_free_nameservers_locked(struct resolv_cache_info* cache_info)
2047{
2048 int i;
2049 for (i = 0; i <= MAXNS; i++) {
2050 free(cache_info->nameservers[i]);
2051 cache_info->nameservers[i] = NULL;
Robert Greenwalt9363d912011-07-25 12:30:17 -07002052 if (cache_info->nsaddrinfo[i] != NULL) {
2053 freeaddrinfo(cache_info->nsaddrinfo[i]);
2054 cache_info->nsaddrinfo[i] = NULL;
2055 }
Mattias Falk23d3e6b2011-04-04 16:12:35 +02002056 }
2057}
2058
Mattias Falk23d3e6b2011-04-04 16:12:35 +02002059void
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05002060_resolv_populate_res_for_net(res_state statp)
Mattias Falkc63e5902011-08-23 14:34:14 +02002061{
Sasha Levitskiyfbae9f32013-02-27 15:48:55 -08002062 if (statp == NULL) {
2063 return;
2064 }
Mattias Falkc63e5902011-08-23 14:34:14 +02002065
Sasha Levitskiyfbae9f32013-02-27 15:48:55 -08002066 pthread_once(&_res_cache_once, _res_cache_init);
2067 pthread_mutex_lock(&_res_cache_list_lock);
2068
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05002069 struct resolv_cache_info* info = _find_cache_info_locked(statp->netid);
Sasha Levitskiyfbae9f32013-02-27 15:48:55 -08002070 if (info != NULL) {
2071 int nserv;
Mattias Falkc63e5902011-08-23 14:34:14 +02002072 struct addrinfo* ai;
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05002073 XLOG("%s: %u\n", __FUNCTION__, statp->netid);
Mattias Falkc63e5902011-08-23 14:34:14 +02002074 for (nserv = 0; nserv < MAXNS; nserv++) {
2075 ai = info->nsaddrinfo[nserv];
2076 if (ai == NULL) {
2077 break;
2078 }
2079
2080 if ((size_t) ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
2081 if (statp->_u._ext.ext != NULL) {
2082 memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen);
2083 statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
2084 } else {
2085 if ((size_t) ai->ai_addrlen
2086 <= sizeof(statp->nsaddr_list[0])) {
2087 memcpy(&statp->nsaddr_list[nserv], ai->ai_addr,
2088 ai->ai_addrlen);
2089 } else {
2090 statp->nsaddr_list[nserv].sin_family = AF_UNSPEC;
2091 }
2092 }
2093 } else {
Szymon Jakubczakea9bf672014-02-14 17:07:23 -05002094 XLOG("%s: found too long addrlen", __FUNCTION__);
Mattias Falkc63e5902011-08-23 14:34:14 +02002095 }
2096 }
2097 statp->nscount = nserv;
2098 // now do search domains. Note that we cache the offsets as this code runs alot
2099 // but the setting/offset-computer only runs when set/changed
2100 strlcpy(statp->defdname, info->defdname, sizeof(statp->defdname));
2101 register char **pp = statp->dnsrch;
2102 register int *p = info->dnsrch_offset;
2103 while (pp < statp->dnsrch + MAXDNSRCH && *p != -1) {
Elliott Hughes37b1b5b2014-07-02 16:27:20 -07002104 *pp++ = &statp->defdname[0] + *p++;
Mattias Falkc63e5902011-08-23 14:34:14 +02002105 }
Mattias Falkc63e5902011-08-23 14:34:14 +02002106 }
Sasha Levitskiyfbae9f32013-02-27 15:48:55 -08002107 pthread_mutex_unlock(&_res_cache_list_lock);
Mattias Falkc63e5902011-08-23 14:34:14 +02002108}