blob: e46bf6270723b705f82437045dcf852918877ea4 [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed 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.
*/
/**
* @file
* This file implements DNSResolver, the object that abstracts
* Domain Name System (DNS) resolution in InetLayer.
*
*/
#include <InetLayer/DNSResolver.h>
#include <InetLayer/InetLayer.h>
#include <InetLayer/InetLayerEvents.h>
#include <string.h>
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <lwip/dns.h>
#include <lwip/tcpip.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
namespace nl {
namespace Inet {
Weave::System::ObjectPool<DNSResolver, INET_CONFIG_NUM_DNS_RESOLVERS> DNSResolver::sPool;
/**
* This method revolves a host name into a list of IP addresses.
*
* @note
* Even if the operation completes successfully,
* the result might be a zero-length list of IP addresses.
* Most of the error generated are returned via the
* application callback.
*
* @param[in] hostName A pointer to a C string representing the host name
* to be queried.
* @param[in] hostNameLen The string length of host name.
* @param[in] maxAddrs The maximum number of addresses to store in the DNS
* table.
* @param[in] addrArray A pointer to the DNS table.
* @param[in] onComplete A pointer to the callback function when a DNS
* request is complete.
* @param[in] appState A pointer to the application state to be passed to
* onComplete when a DNS request is complete.
*
* @retval INET_NO_ERROR if a DNS request is handled
* successfully.
*
* @retval INET_ERROR_NOT_IMPLEMENTED if DNS resolution is not enabled on
* the underlying platform.
*
* @retval _other_ if other POSIX network or OS error
* was returned by the underlying DNS
* resolver implementation.
*
*/
INET_ERROR DNSResolver::Resolve(const char *hostName, uint16_t hostNameLen, uint8_t maxAddrs, IPAddress *addrArray,
DNSResolver::OnResolveCompleteFunct onComplete, void *appState)
{
#if !WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !LWIP_DNS
Release();
return INET_ERROR_NOT_IMPLEMENTED;
#endif // !WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !LWIP_DNS
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS || (WEAVE_SYSTEM_CONFIG_USE_LWIP && LWIP_DNS)
// TODO: Eliminate the need for a local buffer when running on LwIP by changing
// the LwIP DNS interface to support non-nul terminated strings.
char hostNameBuf[NL_DNS_HOSTNAME_MAX_LEN + 1]; // DNS limits hostnames to 253 max characters.
memcpy(hostNameBuf, hostName, hostNameLen);
hostNameBuf[hostNameLen] = 0;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
INET_ERROR res = INET_NO_ERROR;
AppState = appState;
if (maxAddrs > INET_CONFIG_MAX_DNS_ADDRS)
maxAddrs = INET_CONFIG_MAX_DNS_ADDRS;
AddrArray = addrArray;
MaxAddrs = maxAddrs;
NumAddrs = 0;
OnComplete = onComplete;
// Lock LwIP stack
LOCK_TCPIP_CORE();
ip_addr_t lwipAddrArray[INET_CONFIG_MAX_DNS_ADDRS];
err_t lwipErr = dns_gethostbyname(hostNameBuf, lwipAddrArray, LwIPHandleResolveComplete, this);
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
if (lwipErr == ERR_OK)
{
Weave::System::Layer& lSystemLayer = SystemLayer();
CopyAddresses(1, lwipAddrArray);
lSystemLayer.PostEvent(*this, kInetEvent_DNSResolveComplete, 0);
}
else if (lwipErr != ERR_INPROGRESS)
{
res = Weave::System::MapErrorLwIP(lwipErr);
Release();
}
return res;
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
INET_ERROR err = INET_NO_ERROR;
struct addrinfo hints;
struct addrinfo *lookupRes = NULL;
int getaddrinfoRes;
NumAddrs = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 6;
hints.ai_flags = AI_ADDRCONFIG;
getaddrinfoRes = getaddrinfo(hostNameBuf, NULL, &hints, &lookupRes);
if (getaddrinfoRes == 0)
{
for (struct addrinfo *addr = lookupRes; addr != NULL && NumAddrs < maxAddrs; addr = addr->ai_next, NumAddrs++)
addrArray[NumAddrs] = IPAddress::FromSockAddr(*addr->ai_addr);
}
else
{
switch (getaddrinfoRes)
{
case EAI_NODATA:
err = INET_NO_ERROR;
break;
case EAI_NONAME:
err = INET_ERROR_HOST_NOT_FOUND;
break;
case EAI_AGAIN:
err = INET_ERROR_DNS_TRY_AGAIN;
break;
case EAI_SYSTEM:
err = Weave::System::MapErrorPOSIX(errno);
break;
default:
err = INET_ERROR_DNS_NO_RECOVERY;
break;
}
}
if (lookupRes != NULL)
freeaddrinfo(lookupRes);
onComplete(appState, err, NumAddrs, addrArray);
Release();
return INET_NO_ERROR;
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS || (WEAVE_SYSTEM_CONFIG_USE_LWIP && LWIP_DNS)
}
/**
* This method cancels DNS requests that are in progress.
*
* @retval INET_NO_ERROR.
*
*/
INET_ERROR DNSResolver::Cancel()
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
// NOTE: LwIP does not support canceling DNS requests that are in progress. As a consequence,
// we can't release the DNSResolver object until LwIP calls us back (because LwIP retains a
// pointer to the the DNSResolver object while the request is active). However, now that the
// application has called Cancel() we have to make sure to NOT call their OnComplete function
// when the request completes.
//
// To ensure the right thing happens, we NULL the OnComplete pointer here, which signals the
// code in HandleResolveComplete() and LwIPHandleResolveComplete() to not interact with the
// application's state data (AddrArray) and to not call the application's callback. This has
// to happen with the LwIP lock held, since LwIPHandleResolveComplete() runs on LwIP's thread.
// Lock LwIP stack
LOCK_TCPIP_CORE();
// Signal that the request has been canceled by clearing the state of the resolver object.
OnComplete = NULL;
AddrArray = NULL;
MaxAddrs = 0;
NumAddrs = 0;
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
// NOTE: DNS lookups can be canceled only when using the asynchronous mode.
InetLayer& inet = Layer();
OnComplete = NULL;
AppState = NULL;
inet.mAsyncDNSResolver.Cancel(*this);
#endif // INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
return INET_NO_ERROR;
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
/**
* This method is called by InetLayer on success, failure, or timeout of a
* DNS request.
*
*/
void DNSResolver::HandleResolveComplete()
{
// Call the application's completion handler if the request hasn't been canceled.
if (OnComplete != NULL)
OnComplete(AppState, (NumAddrs > 0) ? INET_NO_ERROR : INET_ERROR_HOST_NOT_FOUND, NumAddrs, AddrArray);
// Release the resolver object.
Release();
}
/**
* This method is called by LwIP network stack on success, failure, or timeout
* of a DNS request.
*
* @param[in] name A pointer to a NULL-terminated C string
* representing the host name that is queried.
* @param[in] ipaddr A pointer to a list of resolved IP addresses.
* @param[in] callback_arg A pointer to the arguments that are passed to
* the callback function.
*
*/
#if LWIP_VERSION_MAJOR > 1
void DNSResolver::LwIPHandleResolveComplete(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
#else // LWIP_VERSION_MAJOR <= 1
void DNSResolver::LwIPHandleResolveComplete(const char *name, ip_addr_t *ipaddr, void *callback_arg)
#endif // LWIP_VERSION_MAJOR <= 1
{
DNSResolver *resolver = (DNSResolver *)callback_arg;
if (resolver != NULL)
{
Weave::System::Layer& lSystemLayer = resolver->SystemLayer();
// Copy the resolved address to the application supplied buffer, but only if the request hasn't been canceled.
if (resolver->OnComplete != NULL)
resolver->CopyAddresses((ipaddr != NULL) ? 1 : 0, ipaddr);
lSystemLayer.PostEvent(*resolver, kInetEvent_DNSResolveComplete, 0);
}
}
/**
* This method copies a list of resolved IP addresses to the DNS table.
*
* @param[in] numAddrs The number of addresses in the list.
* @param[in] addrs A list of resolved IP addresses.
*
*/
void DNSResolver::CopyAddresses(uint8_t numAddrs, const ip_addr_t *addrs)
{
if (numAddrs > MaxAddrs)
numAddrs = MaxAddrs;
for (uint8_t i = 0; i < numAddrs; i++)
{
#if LWIP_VERSION_MAJOR > 1
AddrArray[i] = IPAddress::FromLwIPAddr(addrs[i]);
#else // LWIP_VERSION_MAJOR <= 1
AddrArray[i] = IPAddress::FromIPv4(addrs[i]);
#endif // LWIP_VERSION_MAJOR <= 1
}
NumAddrs = numAddrs;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
void DNSResolver::HandleAsyncResolveComplete(void)
{
// Copy the resolved address to the application supplied buffer, but only if the request hasn't been canceled.
if (OnComplete && mState != kState_Canceled)
{
OnComplete(AppState, asyncDNSResolveResult, NumAddrs, AddrArray);
}
Release();
}
#endif // INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
} // namespace Inet
} // namespace nl