/* 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.0.84Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-28)
¤
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.