/* add a char to a bounded buffer */ char*
_bprint_c( char* p, char* end, int c )
{ if (p < end) { if (p+1 == end)
*p++ = 0; else {
*p++ = (char) c;
*p = 0;
}
} return p;
}
/* add a sequence of bytes to a bounded buffer */ char*
_bprint_b( char* p, char* end, constchar* buf, int len )
{ int avail = end - p;
if (avail <= 0 || len <= 0) return p;
if (avail > len)
avail = len;
memcpy( p, buf, avail );
p += avail;
if (p < end)
p[0] = 0; else
end[-1] = 0;
return p;
}
/* add a string to a bounded buffer */ char*
_bprint_s( char* p, char* end, constchar* str )
{ return _bprint_b(p, end, str, strlen(str));
}
/* add a formatted string to a bounded buffer */ char* _bprint( char* p, char* end, constchar* format, ... ) __DEBUG__; char* _bprint( char* p, char* end, constchar* format, ... )
{ int avail, n;
va_list args;
avail = end - p;
if (avail <= 0) return p;
va_start(args, format);
n = vsnprintf( p, avail, format, args);
va_end(args);
/* certain C libraries return -1 in case of truncation */ if (n < 0 || n > avail)
n = avail;
p += n; /* certain C libraries do not zero-terminate in case of truncation */ if (p == end)
p[-1] = 0;
return p;
}
/* add a hex value to a bounded buffer, up to 8 digits */ char*
_bprint_hex( char* p, char* end, unsigned value, int numDigits )
{ char text[sizeof(unsigned)*2]; int nn = 0;
/* add the hexadecimal dump of some memory area to a bounded buffer */ char*
_bprint_hexdump( char* p, char* end, const uint8_t* data, int datalen )
{ int lineSize = 16;
while (datalen > 0) { int avail = datalen; int nn;
if (avail > lineSize)
avail = lineSize;
for (nn = 0; nn < avail; nn++) { if (nn > 0)
p = _bprint_c(p, end, ' ');
p = _bprint_hex(p, end, data[nn], 2);
} for ( ; nn < lineSize; nn++ ) {
p = _bprint_s(p, end, " ");
}
p = _bprint_s(p, end, " ");
for (nn = 0; nn < avail; nn++) { int c = data[nn];
if (c < 32 || c > 127)
c = '.';
p = _bprint_c(p, end, c);
}
p = _bprint_c(p, end, '\n');
data += avail;
datalen -= avail;
} return p;
}
/* dump the content of a query of packet to the log */ void XLOG_BYTES( constvoid* base, int len ) __DEBUG__; void XLOG_BYTES( constvoid* base, int len )
{ if (DEBUG_DATA) { char buff[1024]; char* p = buff, *end = p + sizeof(buff);
p = _bprint_hexdump(p, end, base, len);
XLOG("%s",buff);
}
} __DEBUG__
/* check bytes in a dns packet. returns 1 on success, 0 on failure. *thecursorisonlyadvancedinthecaseofsuccess
*/ staticint
_dnsPacket_checkBytes( DnsPacket* packet, int numBytes, constvoid* bytes )
{ const uint8_t* p = packet->cursor;
if (p + numBytes > packet->end) return0;
if (memcmp(p, bytes, numBytes) != 0) return0;
packet->cursor = p + numBytes; return1;
}
/* parse and skip a given QNAME stored in a query packet, *fromthecurrentcursorposition.returns1onsuccess, *or0formalformeddata.
*/ staticint
_dnsPacket_checkQName( DnsPacket* packet )
{ const uint8_t* p = packet->cursor; const uint8_t* end = packet->end;
for (;;) { int c;
if (p >= end) break;
c = *p++;
if (c == 0) {
packet->cursor = p; return1;
}
/* we don't expect label compression in QNAMEs */ if (c >= 64) break;
p += c; /* we rely on the bound check at the start
* of the loop here */
} /* malformed data */
XLOG("malformed QNAME"); return0;
}
/* parse and skip a given QR stored in a packet. *returns1onsuccess,and0onfailure
*/ staticint
_dnsPacket_checkQR( DnsPacket* packet )
{ if (!_dnsPacket_checkQName(packet)) return0;
/* TYPE must be one of the things we support */ if (!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_A) &&
!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_PTR) &&
!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_MX) &&
!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_AAAA) &&
!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_ALL))
{
XLOG("unsupported TYPE"); return0;
} /* CLASS must be IN */ if (!_dnsPacket_checkBytes(packet, 2, DNS_CLASS_IN)) {
XLOG("unsupported CLASS"); return0;
}
return1;
}
/* check the header of a DNS Query packet, return 1 if it is one *typeofquerywecancache,or0otherwise
*/ staticint
_dnsPacket_checkQuery( DnsPacket* packet )
{ const uint8_t* p = packet->base; int qdCount, anCount, dnCount, arCount;
if (p + DNS_HEADER_SIZE > packet->end) {
XLOG("query packet too small"); return0;
}
/* QR must be set to 0, opcode must be 0 and AA must be 0 */ /* RA, Z, and RCODE must be 0 */ if ((p[2] & 0xFC) != 0 || (p[3] & 0xCF) != 0) {
XLOG("query packet flags unsupported"); return0;
}
/* Note that we ignore the TC, RD, CD, and AD bits here for the *followingreasons: * *-thereisnopointforaquerypacketsenttoaserver *tohavetheTCbitset,buttheimplementationmight *setthebitinthequerybufferforitsownneeds *betweena_resolv_cache_lookupanda *_resolv_cache_add.Weshouldnotfreakoutifthis *isthecase. * *-weconsiderthattheresultfromaquerymightdependon *theRD,AD,andCDbits,sothesebits *shouldbeusedtodifferentiatecachedresult. * *thisimpliesthatthesebitsarecheckedwhenhashingor *comparingquerypackets,butnotTC
*/
/* dump QNAME */
p = _dnsPacket_bprintQName(packet, p, end);
/* dump TYPE */
p = _bprint_s(p, end, " (");
for (nn = 0; qTypes[nn].typeBytes != NULL; nn++) { if (_dnsPacket_checkBytes(packet, 2, qTypes[nn].typeBytes)) {
typeString = qTypes[nn].typeString; break;
}
}
if (typeString != NULL)
p = _bprint_s(p, end, typeString); else { int typeCode = _dnsPacket_readInt16(packet);
p = _bprint(p, end, "UNKNOWN-%d", typeCode);
}
p = _bprint_c(p, end, ')');
/* skip CLASS */
_dnsPacket_skip(packet, 2); return p;
}
/* this function assumes the packet has already been checked */ staticchar*
_dnsPacket_bprintQuery( DnsPacket* packet, char* p, char* end )
{ int qdCount;
if (packet->base[2] & 0x1) {
p = _bprint_s(p, end, "RECURSIVE ");
}
/* we ignore the TC bit for reasons explained in *_dnsPacket_checkQuery(). * *howeverwehashtheRDbittodifferentiate *betweenanswersforrecursiveandnon-recursive *queries.
*/
hash = hash*FNV_MULT ^ (packet->base[2] & 1);
/* mark the first header byte as processed */
_dnsPacket_skip(packet, 1);
/* process the second header byte */
hash = _dnsPacket_hashBytes(packet, 1, hash);
/* cache entry. for simplicity, 'hash' and 'hlink' are inlined in this *structurethoughtheyareconceptuallypartofthehashtable. * *similarly,mru_nextandmru_prevarepartoftheglobalMRUlist
*/ typedefstruct Entry { unsignedint hash; /* hash value */ struct Entry* hlink; /* next in collision chain */ struct Entry* mru_prev; struct Entry* mru_next;
const uint8_t* query; int querylen; const uint8_t* answer; int answerlen;
time_t expires; /* time_t when the entry isn't valid any more */ int id; /* for debugging purpose */
} Entry;
/** *FindtheTTLforanegativeDNSresult.Thisisdefinedastheminimum *oftheSOArecordsTTLandtheMINIMUM-TTLfield(RFC-2308). * *Return0ifnotfound.
*/ static u_long
answer_getNegativeTTL(ns_msg handle) { int n, nscount;
u_long result = 0;
ns_rr rr;
nscount = ns_msg_count(handle, ns_s_ns); for (n = 0; n < nscount; n++) { if ((ns_parserr(&handle, ns_s_ns, n, &rr) == 0) && (ns_rr_type(rr) == ns_t_soa)) { const u_char *rdata = ns_rr_rdata(rr); // find the data const u_char *edata = rdata + ns_rr_rdlen(rr); // add the len to find the end int len;
u_long ttl, rec_result = ns_rr_ttl(rr);
// find the MINIMUM-TTL field from the blob of binary data for this record // skip the server name
len = dn_skipname(rdata, edata); if (len == -1) continue; // error skipping
rdata += len;
// skip the admin name
len = dn_skipname(rdata, edata); if (len == -1) continue; // error skipping
rdata += len;
if (edata - rdata != 5*NS_INT32SZ) continue; // skip: serial number + refresh interval + retry interval + expiry
rdata += NS_INT32SZ * 4; // finally read the MINIMUM TTL
ttl = ns_get32(rdata); if (ttl < rec_result) {
rec_result = ttl;
} // Now that the record is read successfully, apply the new min TTL if (n == 0 || rec_result < result) {
result = rec_result;
}
}
} return result;
}
static __inline__ void entry_mru_add(Entry* e, Entry* list) {
Entry* first = list->mru_next;
e->mru_next = first;
e->mru_prev = list;
list->mru_next = e;
first->mru_prev = e;
}
/* compute the hash of a given entry, this is a hash of most
* data in the query (key) */ staticunsigned
entry_hash( const Entry* e )
{
DnsPacket pack[1];
/* initialize an Entry as a search key, this also checks the input query packet
* returns 1 on success, or 0 in case of unsupported/malformed data */ staticint
entry_init_key( Entry* e, constvoid* query, int querylen )
{
DnsPacket pack[1];
// lock protecting everything in the _resolve_cache_info structs (next ptr, etc) static pthread_mutex_t _res_cache_list_lock;
/* gets cache associated with a network, or NULL if none exists */ staticstruct resolv_cache* _find_named_cache_locked(unsigned netid);
staticvoid
_cache_flush_pending_requests_locked( struct resolv_cache* cache )
{ struct pending_req_info *ri, *tmp; if (cache) {
ri = cache->pending_requests.next;
while (ri) {
tmp = ri;
ri = ri->next;
pthread_cond_broadcast(&tmp->cond);
pthread_cond_destroy(&tmp->cond);
free(tmp);
}
cache->pending_requests.next = NULL;
}
}
/* Return 0 if no pending request is found matching the key. *Ifamatchingrequestisfoundthecallingthreadwillwaituntil
* the matching request completes, then update *cache and return 1. */ staticint
_cache_check_pending_request_locked( struct resolv_cache** cache, Entry* key, unsignednetid )
{ struct pending_req_info *ri, *prev; int exist = 0;
if (*cache && key) {
ri = (*cache)->pending_requests.next;
prev = &(*cache)->pending_requests; while (ri) { if (ri->hash == key->hash) {
exist = 1; break;
}
prev = ri;
ri = ri->next;
}
if (!exist) {
ri = calloc(1, sizeof(struct pending_req_info)); if (ri) {
ri->hash = key->hash;
pthread_cond_init(&ri->cond, NULL);
prev->next = ri;
}
} else { struct timespec ts = {0,0};
XLOG("Waiting for previous request");
ts.tv_sec = _time_now() + PENDING_REQUEST_TIMEOUT;
pthread_cond_timedwait(&ri->cond, &_res_cache_list_lock, &ts); /* Must update *cache as it could have been deleted. */
*cache = _find_named_cache_locked(netid);
}
}
return exist;
}
/* notify any waiting thread that waiting on a request
* matching the key has been added to the cache */ staticvoid
_cache_notify_waiting_tid_locked( struct resolv_cache* cache, Entry* key )
{ struct pending_req_info *ri, *prev;
if (cache && key) {
ri = cache->pending_requests.next;
prev = &cache->pending_requests; while (ri) { if (ri->hash == key->hash) {
pthread_cond_broadcast(&ri->cond); break;
}
prev = ri;
ri = ri->next;
}
// remove item from list and destroy if (ri) {
prev->next = ri->next;
pthread_cond_destroy(&ri->cond);
free(ri);
}
}
}
/* notify the cache that the query failed */ void
_resolv_cache_query_failed( unsigned netid, constvoid* query, int querylen)
{
Entry key[1];
Cache* cache;
if (!entry_init_key(key, query, querylen)) return;
pthread_mutex_lock(&_res_cache_list_lock);
cache = _find_named_cache_locked(netid);
if (cache) {
_cache_notify_waiting_tid_locked(cache, key);
}
XLOG("*************************\n" "*** DNS CACHE FLUSHED ***\n" "*************************");
}
staticint
_res_cache_get_max_entries( void )
{ int cache_size = CONFIG_MAX_ENTRIES;
constchar* cache_mode = getenv("ANDROID_DNS_MODE"); if (cache_mode == NULL || strcmp(cache_mode, "local") != 0) { // Don't use the cache in local mode. This is used by the proxy itself.
cache_size = 0;
}
p = _bprint(temp, end, "MRU LIST (%2d): ", cache->num_entries); for (e = cache->mru_list.mru_next; e != &cache->mru_list; e = e->mru_next)
p = _bprint(p, end, " %d", e->id);
XLOG("%s", temp);
}
staticvoid
_dump_answer(constvoid* answer, int answerlen)
{
res_state statep;
FILE* fp; char* buf; int fileLen;
/* This function tries to find a key within the hash table *Incaseofsuccess,itwillreturna*pointer*tothehashedkey. *Incaseoffailure,itwillreturna*pointer*toNULL * *So,thecallermustcheck'*result'tocheckforsuccess/failure. * *Themainideaisthattheresultcanlaterbeuseddirectlyin *callsto_resolv_cache_addor_resolv_cache_removeasthe'lookup' *parameter.Thismakesthecodesimplerandavoidsre-searching *forthekeypositioninthehtable. * *Theresultofalookup_pisonlyvaliduntilyoualterthehash *table.
*/ static Entry**
_cache_lookup_p( Cache* cache,
Entry* key )
{ int index = key->hash % cache->max_entries;
Entry** pnode = (Entry**) &cache->entries[ index ];
while (*pnode != NULL) {
Entry* node = *pnode;
if (node == NULL) break;
if (node->hash == key->hash && entry_equals(node, key)) break;
pnode = &node->hlink;
} return pnode;
}
/* Add a new entry to the hash table. 'lookup' must be the *resultofanimmediatepreviousfailed_lookup_p()call *(i.e.with*lookup==NULL),and'e'isthepointertothe *newlycreatedentry
*/ staticvoid
_cache_add_p( Cache* cache,
Entry** lookup,
Entry* e )
{
*lookup = e;
e->id = ++cache->last_id;
entry_mru_add(e, &cache->mru_list);
cache->num_entries += 1;
/* Remove the oldest entry from the hash table.
*/ staticvoid
_cache_remove_oldest( Cache* cache )
{
Entry* oldest = cache->mru_list.mru_prev;
Entry** lookup = _cache_lookup_p(cache, oldest);
if (*lookup == NULL) { /* should not happen */
XLOG("%s: OLDEST NOT IN HTABLE ?", __FUNCTION__); return;
} if (DEBUG) {
XLOG("Cache full - removing oldest");
XLOG_QUERY(oldest->query, oldest->querylen);
}
_cache_remove_p(cache, lookup);
}
/* Remove all expired entries from the hash table.
*/ staticvoid _cache_remove_expired(Cache* cache) {
Entry* e;
time_t now = _time_now();
for (e = cache->mru_list.mru_next; e != &cache->mru_list;) { // Entry is old, remove if (now >= e->expires) {
Entry** lookup = _cache_lookup_p(cache, e); if (*lookup == NULL) { /* should not happen */
XLOG("%s: ENTRY NOT IN HTABLE ?", __FUNCTION__); return;
}
e = e->mru_next;
_cache_remove_p(cache, lookup);
} else {
e = e->mru_next;
}
}
}
ResolvCacheStatus
_resolv_cache_lookup( unsigned netid, constvoid* query, int querylen, void* answer, int answersize, int *answerlen )
{
Entry key[1];
Entry** lookup;
Entry* e;
time_t now;
Cache* cache;
cache = _find_named_cache_locked(netid); if (cache == NULL) {
result = RESOLV_CACHE_UNSUPPORTED; gotoExit;
}
/* see the description of _lookup_p to understand this. *thefunctionalwaysreturnanon-NULLpointer.
*/
lookup = _cache_lookup_p(cache, key);
e = *lookup;
if (e == NULL) {
XLOG( "NOT IN CACHE"); // calling thread will wait if an outstanding request is found // that matching this query if (!_cache_check_pending_request_locked(&cache, key, netid) || cache == NULL) { gotoExit;
} else {
lookup = _cache_lookup_p(cache, key);
e = *lookup; if (e == NULL) { gotoExit;
}
}
}
now = _time_now();
/* remove stale entries here */ if (now >= e->expires) {
XLOG( " NOT IN CACHE (STALE ENTRY %p DISCARDED)", *lookup );
XLOG_QUERY(e->query, e->querylen);
_cache_remove_p(cache, lookup); gotoExit;
}
*answerlen = e->answerlen; if (e->answerlen > answersize) { /* NOTE: we return UNSUPPORTED if the answer buffer is too short */
result = RESOLV_CACHE_UNSUPPORTED;
XLOG(" ANSWER TOO LONG"); gotoExit;
}
memcpy( answer, e->answer, e->answerlen );
/* bump up this entry to the top of the MRU list */ if (e != cache->mru_list.mru_next) {
entry_mru_remove( e );
entry_mru_add( e, &cache->mru_list );
}
XLOG( "FOUND IN CACHE entry=%p", e );
result = RESOLV_CACHE_FOUND;
lookup = _cache_lookup_p(cache, key);
e = *lookup;
if (e != NULL) { /* should not happen */
XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD",
__FUNCTION__, e); gotoExit;
}
if (cache->num_entries >= cache->max_entries) {
_cache_remove_expired(cache); if (cache->num_entries >= cache->max_entries) {
_cache_remove_oldest(cache);
} /* need to lookup again */
lookup = _cache_lookup_p(cache, key);
e = *lookup; if (e != NULL) {
XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD",
__FUNCTION__, e); gotoExit;
}
}
ttl = answer_getTTL(answer, answerlen); if (ttl > 0) {
e = entry_alloc(key, answer, answerlen); if (e != NULL) {
e->expires = ttl + _time_now();
_cache_add_p(cache, lookup, e);
}
} #if DEBUG
_cache_dump_mru(cache); #endif Exit: if (cache != NULL) {
_cache_notify_waiting_tid_locked(cache, key);
}
pthread_mutex_unlock(&_res_cache_list_lock);
}
// Head of the list of caches. Protected by _res_cache_list_lock. staticstruct resolv_cache_info _res_cache_list;
/* insert resolv_cache_info into the list of resolv_cache_infos */ staticvoid _insert_cache_info_locked(struct resolv_cache_info* cache_info); /* creates a resolv_cache_info */ staticstruct resolv_cache_info* _create_cache_info( void ); /* gets a resolv_cache_info associated with a network, or NULL if not found */ staticstruct resolv_cache_info* _find_cache_info_locked(unsigned netid); /* look up the named cache, and creates one if needed */ staticstruct resolv_cache* _get_res_cache_for_net_locked(unsigned netid); /* empty the named cache */ staticvoid _flush_cache_for_net_locked(unsigned netid); /* empty the nameservers set for the named cache */ staticvoid _free_nameservers_locked(struct resolv_cache_info* cache_info); /* return 1 if the provided list of name servers differs from the list of name servers
* currently attached to the provided cache_info */ staticint _resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info, constchar** servers, int numservers); /* clears the stats samples contained withing the given cache_info */ staticvoid _res_cache_clear_stats_locked(struct resolv_cache_info* cache_info);
// Parse the addresses before actually locking or changing any state, in case there is an error. // As a side effect this also reduces the time the lock is kept. struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_flags = AI_NUMERICHOST
};
snprintf(sbuf, sizeof(sbuf), "%u", NAMESERVER_PORT); for (unsigned i = 0; i < numservers; i++) { // The addrinfo structures allocated here are freed in _free_nameservers_locked(). int rt = getaddrinfo(servers[i], sbuf, &hints, &nsaddrinfo[i]); if (rt != 0) { for (unsigned j = 0 ; j < i ; j++) {
freeaddrinfo(nsaddrinfo[j]);
nsaddrinfo[j] = NULL;
}
XLOG("%s: getaddrinfo(%s)=%s", __FUNCTION__, servers[i], gai_strerror(rt)); return EINVAL;
}
}
if (!_resolv_is_nameservers_equal_locked(cache_info, servers, numservers)) { // free current before adding new
_free_nameservers_locked(cache_info); unsigned i; for (i = 0; i < numservers; i++) {
cache_info->nsaddrinfo[i] = nsaddrinfo[i];
cache_info->nameservers[i] = strdup(servers[i]);
XLOG("%s: netid = %u, addr = %s\n", __FUNCTION__, netid, servers[i]);
}
cache_info->nscount = numservers;
// Clear the NS statistics because the mapping to nameservers might have changed.
_res_cache_clear_stats_locked(cache_info);
// increment the revision id to ensure that sample state is not written back if the // servers change; in theory it would suffice to do so only if the servers or // max_samples actually change, in practice the overhead of checking is higher than the // cost, and overflows are unlikely
++cache_info->revision_id;
} else { if (cache_info->params.max_samples != old_max_samples) { // If the maximum number of samples changes, the overhead of keeping the most recent // samples around is not considered worth the effort, so they are cleared instead. // All other parameters do not affect shared state: Changing these parameters does // not invalidate the samples, as they only affect aggregation and the conditions // under which servers are considered usable.
_res_cache_clear_stats_locked(cache_info);
++cache_info->revision_id;
} for (unsigned j = 0; j < numservers; j++) {
freeaddrinfo(nsaddrinfo[j]);
}
}
// Always update the search paths, since determining whether they actually changed is // complex due to the zero-padding, and probably not worth the effort. Cache-flushing // however is not // necessary, since the stored cache entries do contain the domain, not // just the host name. // code moved from res_init.c, load_domain_search_list
strlcpy(cache_info->defdname, domains, sizeof(cache_info->defdname)); if ((cp = strchr(cache_info->defdname, '\n')) != NULL)
*cp = '\0';
cp = cache_info->defdname;
offset = cache_info->dnsrch_offset; while (offset < cache_info->dnsrch_offset + MAXDNSRCH) { while (*cp == ' ' || *cp == '\t') /* skip leading white space */
cp++; if (*cp == '\0') /* stop if nothing more to do */ break;
*offset++ = cp - cache_info->defdname; /* record this search domain */ while (*cp) { /* zero-terminate it */ if (*cp == ' '|| *cp == '\t') {
*cp++ = '\0'; break;
}
cp++;
}
}
*offset = -1; /* cache_info->dnsrch_offset has MAXDNSRCH+1 items */
}
staticint
_resolv_is_nameservers_equal_locked(struct resolv_cache_info* cache_info, constchar** servers, int numservers)
{ if (cache_info->nscount != numservers) { return0;
}
// Compare each name server against current name servers. // TODO: this is incorrect if the list of current or previous nameservers // contains duplicates. This does not really matter because the framework // filters out duplicates, but we should probably fix it. It's also // insensitive to the order of the nameservers; we should probably fix that // too. for (int i = 0; i < numservers; i++) { for (int j = 0 ; ; j++) { if (j >= numservers) { return0;
} if (strcmp(cache_info->nameservers[i], servers[j]) == 0) { break;
}
}
}
return1;
}
staticvoid
_free_nameservers_locked(struct resolv_cache_info* cache_info)
{ int i; for (i = 0; i < cache_info->nscount; i++) {
free(cache_info->nameservers[i]);
cache_info->nameservers[i] = NULL; if (cache_info->nsaddrinfo[i] != NULL) {
freeaddrinfo(cache_info->nsaddrinfo[i]);
cache_info->nsaddrinfo[i] = NULL;
}
cache_info->nsstats[i].sample_count =
cache_info->nsstats[i].sample_next = 0;
}
cache_info->nscount = 0;
_res_cache_clear_stats_locked(cache_info);
++cache_info->revision_id;
}
struct resolv_cache_info* info = _find_cache_info_locked(netid); if (info) { if (info->nscount > MAXNS) {
pthread_mutex_unlock(&_res_cache_list_lock);
XLOG("%s: nscount %d > MAXNS %d", __FUNCTION__, info->nscount, MAXNS);
errno = EFAULT; return -1;
} int i; for (i = 0; i < info->nscount; i++) { // Verify that the following assumptions are held, failure indicates corruption: // - getaddrinfo() may never return a sockaddr > sockaddr_storage // - all addresses are valid // - there is only one address per addrinfo thanks to numeric resolution int addrlen = info->nsaddrinfo[i]->ai_addrlen; if (addrlen < (int) sizeof(struct sockaddr) ||
addrlen > (int) sizeof(servers[0])) {
pthread_mutex_unlock(&_res_cache_list_lock);
XLOG("%s: nsaddrinfo[%d].ai_addrlen == %d", __FUNCTION__, i, addrlen);
errno = EMSGSIZE; return -1;
} if (info->nsaddrinfo[i]->ai_addr == NULL) {
pthread_mutex_unlock(&_res_cache_list_lock);
XLOG("%s: nsaddrinfo[%d].ai_addr == NULL", __FUNCTION__, i);
errno = ENOENT; return -1;
} if (info->nsaddrinfo[i]->ai_next != NULL) {
pthread_mutex_unlock(&_res_cache_list_lock);
XLOG("%s: nsaddrinfo[%d].ai_next != NULL", __FUNCTION__, i);
errno = ENOTUNIQ; return -1;
}
}
*nscount = info->nscount; for (i = 0; i < info->nscount; i++) {
memcpy(&servers[i], info->nsaddrinfo[i]->ai_addr, info->nsaddrinfo[i]->ai_addrlen);
stats[i] = info->nsstats[i];
} for (i = 0; i < MAXDNSRCH; i++) { constchar* cur_domain = info->defdname + info->dnsrch_offset[i]; // dnsrch_offset[i] can either be -1 or point to an empty string to indicate the end // of the search offsets. Checking for < 0 is not strictly necessary, but safer. // TODO: Pass in a search domain array instead of a string to // _resolv_set_nameservers_for_net() and make this double check unnecessary. if (info->dnsrch_offset[i] < 0 ||
((size_t)info->dnsrch_offset[i]) >= sizeof(info->defdname) || !cur_domain[0]) { break;
}
strlcpy(domains[i], cur_domain, MAXDNSRCHPATH);
}
*dcount = i;
*params = info->params;
revision_id = info->revision_id;
}
void
_resolv_cache_add_resolver_stats_sample( unsigned netid, int revision_id, int ns, conststruct __res_sample* sample, int max_samples) { if (max_samples <= 0) return;
pthread_mutex_lock(&_res_cache_list_lock);
struct resolv_cache_info* info = _find_cache_info_locked(netid);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.