/**************************************************************************** * libc/netdb/lib_dnsclient.c * DNS host name to IP address resolver. * * The uIP DNS resolver functions are used to lookup a hostname and * map it to a numerical IP address. * * Copyright (C) 2007, 2009, 2012, 2014-2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Based heavily on portions of uIP: * * Author: Adam Dunkels * Copyright (c) 2002-2003, Adam Dunkels. * All rights reserved. * * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netdb/lib_dns.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* The maximum number of retries when asking for a name */ #define MAX_RETRIES 8 /* Buffer sizes */ #define SEND_BUFFER_SIZE 64 #define RECV_BUFFER_SIZE CONFIG_NETDB_DNSCLIENT_MAXRESPONSE /* Use clock monotonic, if possible */ #ifdef CONFIG_CLOCK_MONOTONIC # define DNS_CLOCK CLOCK_MONOTONIC #else # define DNS_CLOCK CLOCK_REALTIME #endif /**************************************************************************** * Private Types ****************************************************************************/ /* This describes either an IPv4 or IPv6 address. It is essentially a named * alternative to sockaddr_storage. */ union dns_server_u { struct sockaddr addr; /* Common address representation */ #ifdef CONFIG_NET_IPv4 struct sockaddr_in ipv4; /* IPv4 address */ #endif #ifdef CONFIG_NET_IPv6 struct sockaddr_in6 ipv6; /* IPv6 address */ #endif }; #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 /* This described one entry in the cache of resolved hostnames */ struct dns_cache_s { #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 time_t ctime; /* Creation time */ #endif char name[CONFIG_NETDB_DNSCLIENT_NAMESIZE]; union dns_server_u addr; /* Resolved address */ }; #endif /**************************************************************************** * Private Data ****************************************************************************/ static sem_t g_dns_sem; /* Protects g_seqno and DNS cache */ static bool g_dns_initialized; /* DNS data structures initialized */ static bool g_dns_address; /* We have the address of the DNS server */ #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 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 */ #endif static uint8_t g_seqno; /* Sequence number of the next request */ /* The DNS server address */ static union dns_server_u g_dns_server; #ifdef CONFIG_NETDB_DNSSERVER_IPv6 /* This is the default IPv6 DNS server address */ static const uint16_t g_ipv6_hostaddr[8] = { HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_1), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_2), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_3), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_4), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_5), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_6), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_7), HTONS(CONFIG_NETDB_DNSSERVER_IPv6ADDR_8) }; #endif #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 /* This is the DNS resolver cache */ static struct dns_cache_s g_dns_cache[CONFIG_NETDB_DNSCLIENT_ENTRIES]; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: dns_semtake * * Description: * Take the DNS semaphore, ignoring errors do to the receipt of signals. * ****************************************************************************/ static void dns_semtake(void) { int errcode = 0; int ret; do { ret = sem_wait(&g_dns_sem); if (ret < 0) { errcode = get_errno(); DEBUGASSERT(errcode == EINTR); } } while (ret < 0 && errcode == EINTR); } /**************************************************************************** * Name: dns_semgive * * Description: * Release the DNS semaphore * ****************************************************************************/ #define dns_semgive() sem_post(&g_dns_sem) /**************************************************************************** * Name: dns_initialize * * Description: * Make sure that the DNS client has been properly initialized for use. * ****************************************************************************/ static bool dns_initialize(void) { /* Have DNS data structures been initialized? */ if (!g_dns_initialized) { sem_init(&g_dns_sem, 0, 1); g_dns_initialized = true; } /* Has the DNS server IP address been assigned? */ if (!g_dns_address) { #if defined(CONFIG_NETDB_DNSSERVER_IPv4) struct sockaddr_in addr4; int ret; /* No, configure the default IPv4 DNS server address */ addr4.sin_family = AF_INET; addr4.sin_port = DNS_DEFAULT_PORT; addr4.sin_addr.s_addr = HTONL(CONFIG_NETDB_DNSSERVER_IPv4ADDR); ret = dns_setserver((FAR struct sockaddr *)&addr4, sizeof(struct sockaddr_in)); if (ret < 0) { return false; } #elif defined(CONFIG_NETDB_DNSSERVER_IPv6) struct sockaddr_in6 addr6; int ret; /* No, configure the default IPv6 DNS server address */ addr6.sin6_family = AF_INET6; addr6.sin6_port = DNS_DEFAULT_PORT; memcpy(addr6.sin6_addr.s6_addr, g_ipv6_hostaddr, 16); ret = dns_setserver((FAR struct sockaddr *)&addr6, sizeof(struct sockaddr_in6)); if (ret < 0) { return false; } #else /* No, then we are not ready to perform DNS queries */ return false; #endif } return true; } /**************************************************************************** * 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 address associated with the hostname * addrlen - The size of the of the IP address. * * Returned Value: * None * ****************************************************************************/ #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 static void dns_save_answer(FAR const char *hostname, FAR const struct sockaddr *addr, socklen_t addrlen) { FAR struct dns_cache_s *entry; #if CONFIG_NETDB_DNSCLIENT_LIFESEC > 0 struct timespec now; #endif int next; int ndx; /* 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 */ (void)clock_settime(DNS_CLOCK, &now); entry->ctime = (time_t)now.tv_sec; #endif strncpy(entry->name, hostname, CONFIG_NETDB_DNSCLIENT_NAMESIZE); memcpy(&entry->addr.addr, addr, addrlen); /* Save the updated head index */ g_dns_head = next; dns_semgive(); } #endif /**************************************************************************** * Name: dns_parse_name * * Description: * Walk through a compact encoded DNS name and return the end of it. * ****************************************************************************/ static FAR uint8_t *dns_parse_name(FAR uint8_t *query) { uint8_t n; do { n = *query++; while (n > 0) { ++query; --n; } } while (*query != 0); return query + 1; } /**************************************************************************** * Name: dns_send_query * * Description: * Runs through the list of names to see if there are any that have * not yet been queried and, if so, sends out a query. * ****************************************************************************/ static int dns_send_query(int sd, FAR const char *name, FAR union dns_server_u *uaddr, uint16_t rectype) { register FAR struct dns_header_s *hdr; FAR uint8_t *dest; FAR uint8_t *nptr; FAR const char *src; uint8_t buffer[SEND_BUFFER_SIZE]; uint8_t seqno; socklen_t addrlen; int errcode; int ret; int n; /* Increment the sequence number */ dns_semtake(); seqno = g_seqno++; dns_semgive(); /* Initialize the request header */ hdr = (FAR struct dns_header_s *)buffer; memset(hdr, 0, sizeof(struct dns_header_s)); hdr->id = htons(seqno); hdr->flags1 = DNS_FLAG1_RD; hdr->numquestions = HTONS(1); dest = buffer + 12; /* Convert hostname into suitable query format. */ src = name - 1; do { /* Copy the name string */ src++; nptr = dest++; for (n = 0; *src != '.' && *src != 0; src++) { *dest++ = *(uint8_t *)src; n++; } /* Pre-pend the name length */ *nptr = n; } while (*src != '\0'); /* Add NUL termination, DNS record type, and DNS class */ *dest++ = '\0'; /* NUL termination */ *dest++ = (rectype >> 8); /* DNS record type (big endian) */ *dest++ = (rectype & 0xff); *dest++ = (DNS_CLASS_IN >> 8); /* DNS record class (big endian) */ *dest++ = (DNS_CLASS_IN & 0xff); /* Send the request */ #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 if (uaddr->addr.sa_family == AF_INET) #endif { addrlen = sizeof(struct sockaddr_in); } #endif #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 else #endif { addrlen = sizeof(struct sockaddr_in6); } #endif ret = sendto(sd, buffer, dest - buffer, 0, &uaddr->addr, addrlen); /* Return the negated errno value on sendto failure */ if (ret < 0) { errcode = get_errno(); ndbg("ERROR: sendto failed: %d\n", errcode); return -errcode; } return OK; } /**************************************************************************** * Name: dns_recv_response * * Description: * Called when new UDP data arrives * ****************************************************************************/ static int dns_recv_response(int sd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { FAR uint8_t *nameptr; char buffer[RECV_BUFFER_SIZE]; FAR struct dns_answer_s *ans; FAR struct dns_header_s *hdr; #if 0 /* Not used */ uint8_t nquestions; #endif uint8_t nanswers; int errcode; int ret; /* Receive the response */ ret = recv(sd, buffer, RECV_BUFFER_SIZE, 0); if (ret < 0) { errcode = get_errno(); ndbg("ERROR: recv failed: %d\n", errcode); return -errcode; } hdr = (FAR struct dns_header_s *)buffer; nvdbg("ID %d\n", htons(hdr->id)); nvdbg("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE); nvdbg("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK); nvdbg("Num questions %d, answers %d, authrr %d, extrarr %d\n", htons(hdr->numquestions), htons(hdr->numanswers), htons(hdr->numauthrr), htons(hdr->numextrarr)); /* Check for error */ if ((hdr->flags2 & DNS_FLAG2_ERR_MASK) != 0) { ndbg("ERROR: DNS reported error: flags2=%02x\n", hdr->flags2); return -EPROTO; } /* We only care about the question(s) and the answers. The authrr * and the extrarr are simply discarded. */ #if 0 /* Not used */ nquestions = htons(hdr->numquestions); #endif nanswers = htons(hdr->numanswers); /* Skip the name in the question. TODO: This should really be * checked against the name in the question, to be sure that they * match. */ #ifdef CONFIG_DEBUG_NET { int d = 64; nameptr = dns_parse_name((uint8_t *)buffer + 12) + 4; for (;;) { ndbg("%02X %02X %02X %02X %02X %02X %02X %02X \n", nameptr[0],nameptr[1],nameptr[2],nameptr[3], nameptr[4],nameptr[5],nameptr[6],nameptr[7]); nameptr += 8; d -= 8; if (d < 0) { break; } } } #endif nameptr = dns_parse_name((uint8_t *)buffer + 12) + 4; for (; nanswers > 0; nanswers--) { /* The first byte in the answer resource record determines if it * is a compressed record or a normal one. */ if (*nameptr & 0xc0) { /* Compressed name. */ nameptr += 2; nvdbg("Compressed answer\n"); } else { /* Not compressed name. */ nameptr = dns_parse_name(nameptr); } ans = (FAR struct dns_answer_s *)nameptr; nvdbg("Answer: type=%04x, class=%04x, ttl=%06x, length=%04x \n", htons(ans->type), htons(ans->class), (htons(ans->ttl[0]) << 16) | htons(ans->ttl[1]), htons(ans->len)); /* Check for IPv4/6 address type and Internet class. Others are discarded. */ #ifdef CONFIG_NET_IPv4 if (ans->type == HTONS(DNS_RECTYPE_A) && ans->class == HTONS(DNS_CLASS_IN) && ans->len == HTONS(4)) { ans->u.ipv4.s_addr = *(FAR uint32_t *)(nameptr + 10); nvdbg("IPv4 address: %d.%d.%d.%d\n", (ans->u.ipv4.s_addr ) & 0xff, (ans->u.ipv4.s_addr >> 8 ) & 0xff, (ans->u.ipv4.s_addr >> 16 ) & 0xff, (ans->u.ipv4.s_addr >> 24 ) & 0xff); if (*addrlen >= sizeof(struct sockaddr_in)) { FAR struct sockaddr_in *inaddr; inaddr = (FAR struct sockaddr_in *)addr; inaddr->sin_family = AF_INET; inaddr->sin_port = 0; inaddr->sin_addr.s_addr = ans->u.ipv4.s_addr; *addrlen = sizeof(struct sockaddr_in); return OK; } else { return -ERANGE; } } else #endif #ifdef CONFIG_NET_IPv6 if (ans->type == HTONS(DNS_RECTYPE_AAAA) && ans->class == HTONS(DNS_CLASS_IN) && ans->len == HTONS(16)) { memcpy(&ans->u.ipv6.s6_addr, nameptr + 10, 16); nvdbg("IPv6 address: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", htons(ans->u.ipv6.s6_addr[7]), htons(ans->u.ipv6.s6_addr[6]), htons(ans->u.ipv6.s6_addr[5]), htons(ans->u.ipv6.s6_addr[4]), htons(ans->u.ipv6.s6_addr[3]), htons(ans->u.ipv6.s6_addr[2]), htons(ans->u.ipv6.s6_addr[1]), htons(ans->u.ipv6.s6_addr[0])); if (*addrlen >= sizeof(struct sockaddr_in6)) { FAR struct sockaddr_in6 *inaddr; inaddr = (FAR struct sockaddr_in6 *)addr; inaddr->sin6_family = AF_INET; inaddr->sin6_port = 0; memcpy(inaddr->sin6_addr.s6_addr, ans->u.ipv6.s6_addr, 16); *addrlen = sizeof(struct sockaddr_in6); return OK; } else { return -ERANGE; } } else #endif { nameptr = nameptr + 10 + htons(ans->len); } } return -EADDRNOTAVAIL; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: dns_bind * * Description: * Initialize the DNS resolver and return a socket bound to the DNS name * server. The name server was previously selected via dns_server(). * * Input Parameters: * None * * Returned Value: * On success, the bound, non-negative socket descriptor is returned. A * negated errno value is returned on any failure. * ****************************************************************************/ int dns_bind(void) { struct timeval tv; int errcode; int sd; int ret; /* Has the DNS client been properly initialized? */ if (!dns_initialize()) { ndbg("ERROR: DNS client has not been initialized\n"); return -EDESTADDRREQ; } /* Create a new socket */ sd = socket(PF_INET, SOCK_DGRAM, 0); if (sd < 0) { errcode = get_errno(); ndbg("ERROR: socket() failed: %d\n", errcode); return -errcode; } /* Set up a receive timeout */ tv.tv_sec = 30; tv.tv_usec = 0; ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); if (ret < 0) { errcode = get_errno(); ndbg("ERROR: setsockopt() failed: %d\n", errcode); close(sd); return -errcode; } return sd; } /**************************************************************************** * Name: dns_query * * Description: * Using the DNS resolver socket (sd), look up the the 'hostname', and * return its IP address in 'ipaddr' * * Input Parameters: * sd - The socket descriptor previously initialized by dsn_bind(). * hostname - The hostname string to be resolved. * addr - The location to return the IP address associated with the * hostname * addrlen - On entry, the size of the buffer backing up the 'addr' * pointer. On return, this location will hold the actual size of * the returned address. * * Returned Value: * Returns zero (OK) if the query was successful. * ****************************************************************************/ int dns_query(int sd, FAR const char *hostname, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { #if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6) int noipv4 = false; int noipv6 = false; #endif int retries; int ret; /* Loop while receive timeout errors occur and there are remaining retries */ for (retries = 0; retries < 3; retries++) { #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 /* If we know that the IPv4 address is not available, then don't try * again. */ if (!noipv4) #endif { /* Send the IPv4 query */ ret = dns_send_query(sd, hostname, &g_dns_server, DNS_RECTYPE_A); if (ret < 0) { ndbg("ERROR: IPv4 dns_send_query failed: %d\n", ret); return ret; } /* Obtain the IPv4 response */ ret = dns_recv_response(sd, addr, addrlen); if (ret >= 0) { /* IPv4 response received successfully */ #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 /* Save the answer in the DNS cache */ dns_save_answer(hostname, addr, *addrlen); #endif return OK; } /* Handle errors */ ndbg("ERROR: IPv4 dns_recv_response failed: %d\n", ret); #ifdef CONFIG_NET_IPv6 if (ret != -EADDRNOTAVAIL) { /* The IPv4 address is not available. */ noipv4 = true; if (noipv6) { /* Neither address is available */ return ret; } } else #endif if (ret != -EAGAIN) { /* Some failure other than receive timeout occurred */ return ret; } } #endif #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 /* If we know that the IPv4 address is not available, then don't try * again. */ if (!noipv6) #endif { /* Send the IPv6 query */ ret = dns_send_query(sd, hostname, &g_dns_server, DNS_RECTYPE_AAAA); if (ret < 0) { ndbg("ERROR: IPv6 dns_send_query failed: %d\n", ret); return ret; } /* Obtain the IPv6 response */ ret = dns_recv_response(sd, addr, addrlen); if (ret >= 0) { /* IPv6 response received successfully */ #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 /* Save the answer in the DNS cache */ dns_save_answer(hostname, addr, *addrlen); #endif return OK; } /* Handle errors */ ndbg("ERROR: IPv6 dns_recv_response failed: %d\n", ret); #ifdef CONFIG_NET_IPv4 if (ret != -EADDRNOTAVAIL) { /* The IPv6 address is not available. */ noipv6 = true; if (noipv4) { /* Neither address is available */ return ret; } } else #endif if (ret != -EAGAIN) { /* Some failure other than receive timeout occurred */ return ret; } } #endif } return -ETIMEDOUT; } /**************************************************************************** * Name: dns_setserver * * Description: * Configure which DNS server to use for queries * ****************************************************************************/ int dns_setserver(FAR const struct sockaddr *addr, socklen_t addrlen) { FAR uint16_t *pport; size_t copylen; DEBUGASSERT(addr != NULL); /* Copy the new server IP address into our private global data structure */ #ifdef CONFIG_NET_IPv4 /* Check for an IPv4 address */ if (addr->sa_family == AF_INET) { /* Set up for the IPv4 address copy */ copylen = sizeof(struct sockaddr_in); pport = &g_dns_server.ipv4.sin_port; } else #endif #ifdef CONFIG_NET_IPv6 /* Check for an IPv6 address */ if (addr->sa_family == AF_INET6) { /* Set up for the IPv6 address copy */ copylen = sizeof(struct sockaddr_in6); pport = &g_dns_server.ipv6.sin6_port; } else #endif { nvdbg("ERROR: Unsupported family: %d\n", addr->sa_family); return -ENOSYS; } /* Copy the IP address */ if (addrlen < copylen) { nvdbg("ERROR: Invalid addrlen %ld for family %d\n", (long)addrlen, addr->sa_family); return -EINVAL; } memcpy(&g_dns_server.addr, addr, copylen); /* A port number of zero means to use the default DNS server port number */ if (*pport == 0) { *pport = HTONS(DNS_DEFAULT_PORT); } /* We now have a valid DNS address */ g_dns_address = true; return OK; } /**************************************************************************** * Name: dns_getserver * * Description: * Obtain the currently configured DNS server. * ****************************************************************************/ int dns_getserver(FAR struct sockaddr *addr, FAR socklen_t *addrlen) { socklen_t copylen; DEBUGASSERT(addr != NULL && addrlen != NULL); #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 if (g_dns_server.addr.sa_family == AF_INET) #endif { copylen = sizeof(struct sockaddr_in); } #endif #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 else #endif { copylen = sizeof(struct sockaddr_in6); } #endif /* Copy the DNS server address to the caller-provided buffer */ if (copylen > *addrlen) { nvdbg("ERROR: addrlen %ld too small for address family %d\n", (long)addrlen, g_dns_server.addr.sa_family); return -EINVAL; } memcpy(addr, &g_dns_server.addr, copylen); *addrlen = copylen; return OK; } /**************************************************************************** * 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 address associated with the * hostname * addrlen - On entry, the size of the buffer backing up the 'addr' * pointer. On return, this location will hold the actual size of * the returned address. * * 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. * ****************************************************************************/ #if CONFIG_NETDB_DNSCLIENT_ENTRIES > 0 int dns_find_answer(FAR const char *hostname, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { 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 (!g_dns_initialized) { ndbg("ERROR: DNS not initialized yet\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_settime(DNS_CLOCK, &now); #endif /* REVISIT: This is not thread safe */ 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) { socklen_t inlen; /* We have a match. Return the resolved host address */ #ifdef CONFIG_NET_IPv4 if (entry->addr.addr.sa_family == AF_INET) #ifdef CONFIG_NET_IPv6 #endif { inlen = sizeof(struct sockaddr_in); } #endif #ifdef CONFIG_NET_IPv6 else #ifdef CONFIG_NET_IPv4 #endif { inlen = sizeof(struct sockaddr_in6); } #endif /* Make sure that the address will fit in the caller-provided * buffer. */ if (*addrlen < inlen) { ret = -ERANGE; goto errout_with_sem; } /* Return the address information */ memcpy(addr, &entry->addr.addr, inlen); *addrlen = inlen; dns_semgive(); return OK; } } } ret = -ENOENT; errout_with_sem: dns_semgive(); return ret; } #endif