nuttx/libs/libc/netdb/lib_dnscache.c
SPRESENSE d1c7e1816f libc/netdb: Separate IPv4 and IPv6 cache size limit
Some domains have a lot of IPv6 addresses. Because of that, it is
not possible to get the IPv4 address with getaddrinfo.

This change separate IPv4 and IPv6 cache size limit to enable to
get both IP addresses.
2024-09-02 06:16:50 -04:00

296 lines
8.3 KiB
C

/****************************************************************************
* 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 <nuttx/config.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#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 */