* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
299 lines
9 KiB
C
299 lines
9 KiB
C
/****************************************************************************
|
|
* libs/libc/netdb/lib_dnscache.c
|
|
*
|
|
* Copyright (C) 2007, 2009, 2012, 2014-2016 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. 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.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* 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"
|
|
|
|
#if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Use clock monotonic, if possible */
|
|
|
|
#ifdef CONFIG_CLOCK_MONOTONIC
|
|
# define DNS_CLOCK CLOCK_MONOTONIC
|
|
#else
|
|
# define DNS_CLOCK CLOCK_REALTIME
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* 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_DNSCLIENT_MAXIP]; /* Resolved address */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* 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:
|
|
* Same 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.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void dns_save_answer(FAR const char *hostname,
|
|
FAR const union dns_addr_u *addr, int naddr)
|
|
{
|
|
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_DNSCLIENT_MAXIP);
|
|
DEBUGASSERT(naddr >= 1 && naddr <= UCHAR_MAX);
|
|
|
|
/* Get exclusive access to the DNS cache */
|
|
|
|
dns_semtake();
|
|
|
|
/* 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, using CLOCK_MONOTONIC if possible */
|
|
|
|
clock_gettime(DNS_CLOCK, &now);
|
|
entry->ctime = (time_t)now.tv_sec;
|
|
#endif
|
|
|
|
strncpy(entry->name, hostname, CONFIG_NETDB_DNSCLIENT_NAMESIZE);
|
|
memcpy(&entry->addr, addr, naddr * sizeof(*addr));
|
|
entry->naddr = naddr;
|
|
|
|
/* Save the updated head index */
|
|
|
|
g_dns_head = next;
|
|
dns_semgive();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
|
|
/* If DNS not initialized, no need to proceed */
|
|
|
|
if (!dns_initialize())
|
|
{
|
|
nerr("ERROR: DNS failed to initialize\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* Get exclusive access to the DNS cache */
|
|
|
|
dns_semtake();
|
|
|
|
#if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0
|
|
/* Get the current time, using CLOCK_MONOTONIC if possible */
|
|
|
|
ret = clock_gettime(DNS_CLOCK, &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)
|
|
{
|
|
/* 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. Notice that
|
|
* 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_semgive();
|
|
return OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = -ENOENT;
|
|
|
|
dns_semgive();
|
|
return ret;
|
|
}
|
|
|
|
#endif /* CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 */
|
|
|