/**************************************************************************** * libs/libc/netdb/lib_dnscache.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include "netdb/lib_dns.h" #include "netdb/lib_netdb.h" #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 /**************************************************************************** * Private Types ****************************************************************************/ /* This described one entry in the cache of resolved hostnames. * * REVISIT: this consumes extra space, especially when multiple * addresses per name are stored. */ struct dns_cache_s { #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 time_t ctime; /* Creation time */ #endif char name[CONFIG_NETDB_DNSCLIENT_NAMESIZE]; uint8_t naddr; /* How many addresses per name */ union dns_addr_u addr[CONFIG_NETDB_MAX_IPADDR]; uint32_t ttl; }; /**************************************************************************** * Private Data ****************************************************************************/ static uint8_t g_dns_head; /* Head of the circular, DNS resolver cache */ static uint8_t g_dns_tail; /* Tail of the circular, DNS resolver cache */ /* This is the DNS resolver cache */ static struct dns_cache_s g_dns_cache[CONFIG_NETDB_DNSCLIENT_ENTRIES]; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: dns_save_answer * * Description: * Save the last resolved hostname in the DNS cache * * Input Parameters: * hostname - The hostname string to be cached. * addr - The IP addresses associated with the hostname. * naddr - The count of the IP addresses. * ttl - The TTL of the IP addresses. * * Returned Value: * None * ****************************************************************************/ void dns_save_answer(FAR const char *hostname, FAR const union dns_addr_u *addr, int naddr, uint32_t ttl) { FAR struct dns_cache_s *entry; #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 struct timespec now; #endif int next; int ndx; naddr = MIN(naddr, CONFIG_NETDB_MAX_IPADDR); DEBUGASSERT(naddr >= 1 && naddr <= UCHAR_MAX); /* Get exclusive access to the DNS cache */ dns_lock(); /* Get the index to the new head of the list */ ndx = g_dns_head; next = ndx + 1; if (next >= CONFIG_NETDB_DNSCLIENT_ENTRIES) { next = 0; } /* If the next head pointer would match the tail index, then increment * the tail index, discarding the oldest mapping in the cache. */ if (next == g_dns_tail) { int tmp = g_dns_tail + 1; if (tmp >= CONFIG_NETDB_DNSCLIENT_ENTRIES) { tmp = 0; } g_dns_tail = tmp; } /* Save the answer in the cache */ entry = &g_dns_cache[ndx]; #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 /* Get the current time */ clock_gettime(CLOCK_MONOTONIC, &now); entry->ctime = (time_t)now.tv_sec; #endif strlcpy(entry->name, hostname, CONFIG_NETDB_DNSCLIENT_NAMESIZE); memcpy(&entry->addr, addr, naddr * sizeof(*addr)); entry->naddr = naddr; entry->ttl = ttl; /* Save the updated head index */ g_dns_head = next; dns_unlock(); } /**************************************************************************** * Name: dns_clear_answer * * Description: * Clear the resolved hostname in the DNS cache * * Returned Value: * None * ****************************************************************************/ void dns_clear_answer(void) { /* Get exclusive access to the DNS cache */ dns_lock(); /* Reset the circular of DNS cache */ g_dns_head = 0; g_dns_tail = 0; dns_unlock(); } /**************************************************************************** * Name: dns_find_answer * * Description: * Check if we already have the resolved hostname address in the cache. * * Input Parameters: * hostname - The hostname string to be resolved. * addr - The location to return the IP addresses associated with the * hostname. * naddr - On entry, the count of addresses backing up the 'addr' * pointer. On return, this location will hold the actual count of * the returned addresses. * * Returned Value: * If the host name was successfully found in the DNS name resolution * cache, zero (OK) will be returned. Otherwise, some negated errno * value will be returned, typically -ENOENT meaning that the hostname * was not found in the cache. * ****************************************************************************/ int dns_find_answer(FAR const char *hostname, FAR union dns_addr_u *addr, FAR int *naddr) { FAR struct dns_cache_s *entry; #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 struct timespec now; uint32_t elapsed; int ret; #endif int next; int ndx; /* Get exclusive access to the DNS cache */ dns_lock(); #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 /* Get the current time */ ret = clock_gettime(CLOCK_MONOTONIC, &now); #endif for (ndx = g_dns_tail; ndx != g_dns_head; ndx = next) { entry = &g_dns_cache[ndx]; /* Advance the index for the next time through the loop, handling * wrapping to the beginning of the circular buffer. */ next = ndx + 1; if (next >= CONFIG_NETDB_DNSCLIENT_ENTRIES) { next = 0; } #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 /* Check if this entry has expired * REVISIT: Does not this calculation assume that the sizeof(time_t) * is equal to the sizeof(uint32_t)? */ elapsed = (uint32_t)now.tv_sec - (uint32_t)entry->ctime; if (ret >= 0 && (elapsed > CONFIG_NETDB_DNSCLIENT_LIFESEC || elapsed > entry->ttl)) { /* This entry has expired. Increment the tail index to exclude * this entry on future traversals. */ g_dns_tail = next; } else #endif { /* The entry has not expired, check for a name match. Because * the names are truncated to CONFIG_NETDB_DNSCLIENT_NAMESIZE, * this has the possibility of aliasing two names and returning * the wrong entry from the cache. */ if (strncmp(hostname, entry->name, CONFIG_NETDB_DNSCLIENT_NAMESIZE) == 0) { /* We have a match. Return the resolved host address */ /* Make sure that the address will fit in the caller-provided * buffer. */ *naddr = MIN(*naddr, entry->naddr); /* Return the address information */ memcpy(addr, &entry->addr, *naddr * sizeof(*addr)); dns_unlock(); return OK; } } } ret = -ENOENT; dns_unlock(); return ret; } #endif /* CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 */