/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "resolv_cache.h"
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "pthread.h"
#include <errno.h>
#include "arpa_nameser.h"
#include <sys/system_properties.h>
#include <net/if.h>
#include <netdb.h>
#include <linux/if.h>
#include <arpa/inet.h rel='nofollow' onclick='return false;'>
#include "resolv_private.h"
#include "resolv_iface.h"
#include "res_private.h"
/* This code implements a small and *simple* DNS resolver cache.
*
* It is only used to cache DNS answers for a time defined by the smallest TTL
* among the answer records in order to reduce DNS traffic. It is not supposed
* to be a full DNS cache, since we plan to implement that in the future in a
* dedicated process running on the system.
*
* Note that its design is kept simple very intentionally, i.e.:
*
* - it takes raw DNS query packet data as input, and returns raw DNS
* answer packet data as output
*
* (this means that two similar queries that encode the DNS name
* differently will be treated distinctly).
*
* the smallest TTL value among the answer records are used as the time
* to keep an answer in the cache.
*
* this is bad, but we absolutely want to avoid parsing the answer packets
* (and should be solved by the later full DNS cache process).
*
* - the implementation is just a (query-data) => (answer-data) hash table
* with a trivial least-recently-used expiration policy.
*
* Doing this keeps the code simple and avoids to deal with a lot of things
* that a full DNS cache is expected to do.
*
* The API is also very simple:
*
* - the client calls _resolv_cache_get() to obtain a handle to the cache.
* this will initialize the cache on first usage. the result can be NULL
* if the cache is disabled.
*
* - the client calls _resolv_cache_lookup() before performing a query
*
* if the function returns RESOLV_CACHE_FOUND, a copy of the answer data
* has been copied into the client-provided answer buffer.
*
* if the function returns RESOLV_CACHE_NOTFOUND, the client should perform
* a request normally, *then* call _resolv_cache_add() to add the received
* answer to the cache.
*
* if the function returns RESOLV_CACHE_UNSUPPORTED, the client should
* perform a request normally, and *not* call _resolv_cache_add()
*
* note that RESOLV_CACHE_UNSUPPORTED is also returned if the answer buffer
* is too short to accomodate the cached result.
*
* - when network settings change, the cache must be flushed since the list
* of DNS servers probably changed. this is done by calling
* _resolv_cache_reset()
*
* the parameter to this function must be an ever-increasing generation
* number corresponding to the current network settings state.
*
* This is done because several threads could detect the same network
* settings change (but at different times) and will all end up calling the
* same function. Comparing with the last used generation number ensures
* that the cache is only flushed once per network change.
*/
/* the name of an environment variable that will be checked the first time
* this code is called if its value is "0", then the resolver cache is
* disabled.
*/
#define CONFIG_ENV "BIONIC_DNSCACHE"
/* entries older than CONFIG_SECONDS seconds are always discarded.
*/
#define CONFIG_SECONDS (60*10) /* 10 minutes */
/* default number of entries kept in the cache. This value has been
* determined by browsing through various sites and counting the number
* of corresponding requests. Keep in mind that our framework is currently
* performing two requests per name lookup (one for IPv4, the other for IPv6)
*
* www.google.com 4
* www.ysearch.com 6
* www.amazon.com 8
* www.nytimes.com 22
* www.espn.com 28
* www.msn.com 28
* www.lemonde.fr 35
*
* (determined in 2009-2-17 from Paris, France, results may vary depending
* on location)
*
* most high-level websites use lots of media/ad servers with different names
* but these are generally reused when browsing through the site.
*
* As such, a value of 64 should be relatively comfortable at the moment.
*
* The system property ro.net.dns_cache_size can be used to override the default
* value with a custom value
*
*
* ******************************************
* * NOTE - this has changed.
* * 1) we've added IPv6 support so each dns query results in 2 responses
* * 2) we've made this a system-wide cache, so the cost is less (it's not
* * duplicated in each process) and the need is greater (more processes
* * making different requests).
* * Upping by 2x for IPv6
* * Upping by another 5x for the centralized nature
* *****************************************
*/
#define CONFIG_MAX_ENTRIES 64 * 2 * 5
/* name of the system property that can be used to set the cache size */
#define DNS_CACHE_SIZE_PROP_NAME "ro.net.dns_cache_size"
/****************************************************************************/
/****************************************************************************/
/***** *****/
/***** *****/
/***** *****/
/****************************************************************************/
/****************************************************************************/
/* set to 1 to debug cache operations */
#define DEBUG 0
/* set to 1 to debug query data */
#define DEBUG_DATA 0
#undef XLOG
#if DEBUG
# include "libc_logging.h"
# define XLOG(...) __libc_format_log(ANDROID_LOG_DEBUG,"libc",__VA_ARGS__)
#include <stdio.h>
#include <stdarg.h>
/** BOUNDED BUFFER FORMATTING
**/
/* technical note:
*
* the following debugging routines are used to append data to a bounded
* buffer they take two parameters that are:
*
* - p : a pointer to the current cursor position in the buffer
* this value is initially set to the buffer's address.
*
* - end : the address of the buffer's limit, i.e. of the first byte
* after the buffer. this address should never be touched.
*
* IMPORTANT: it is assumed that end > buffer_address, i.e.
* that the buffer is at least one byte.
*
* the _bprint_() functions return the new value of 'p' after the data
* has been appended, and also ensure the following:
*
* - the returned value will never be strictly great