Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hostByName timeout fixes #8787

Merged
merged 10 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cores/esp8266/IPAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ IPAddress::IPAddress(const IPAddress& from)
ip_addr_copy(_ip, from._ip);
}

IPAddress::IPAddress(IPAddress&& from)
{
ip_addr_copy(_ip, from._ip);
}

IPAddress::IPAddress() {
_ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address
}
Expand Down
4 changes: 3 additions & 1 deletion cores/esp8266/IPAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ class IPAddress: public Printable {
public:
// Constructors
IPAddress();
IPAddress(const IPAddress& from);
IPAddress(const IPAddress&);
IPAddress(IPAddress&&);

IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
IPAddress(uint32_t address) { ctor32(address); }
IPAddress(unsigned long address) { ctor32(address); }
Expand Down
183 changes: 91 additions & 92 deletions libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@

*/

#include <cstring>
#include <list>
#include <string.h>
#include <memory>
#include <type_traits>

#include <coredecls.h>
#include <PolledTimeout.h>
#include "ESP8266WiFi.h"
Expand Down Expand Up @@ -595,112 +598,105 @@ bool ESP8266WiFiGenericClass::isSleepLevelMax () {
// ------------------------------------------------ Generic Network function ---------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------

void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
namespace {

static bool _dns_lookup_pending = false;
struct _dns_found_result {
IPAddress addr;
bool done;
};

/**
* Resolve the given hostname to an IP address.
* @param aHostname Name to be resolved
* @param aResult IPAddress structure to store the returned IP address
* @return 1 if aIPAddrString was successfully converted to an IP address,
* else 0
*/
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
{
return hostByName(aHostname, aResult, 10000);
}

static void _dns_found_callback(const char *, const ip_addr_t *, void *);

int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms)
{
ip_addr_t addr;
aResult = static_cast<uint32_t>(INADDR_NONE);

if(aResult.fromString(aHostname)) {
// Host name is a IP address use it!
DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname);
static int hostByNameImpl(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms, DNSResolveType resolveType) {
if (aResult.fromString(aHostname)) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s is IP!\n", aHostname);
return 1;
}

static_assert(std::is_same_v<uint8_t, std::underlying_type_t<decltype(resolveType)>>, "");
DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname);
#if LWIP_IPV4 && LWIP_IPV6
err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult,LWIP_DNS_ADDRTYPE_DEFAULT);
#else
err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult);
#endif
if(err == ERR_OK) {
aResult = IPAddress(&addr);
} else if(err == ERR_INPROGRESS) {
_dns_lookup_pending = true;
// Will resume on timeout or when wifi_dns_found_callback fires.
// The final argument, intvl_ms, to esp_delay influences how frequently
// the scheduled recurrent functions (Schedule.h) are probed; here, to allow
// the ethernet driver perform work.
esp_delay(timeout_ms, []() { return _dns_lookup_pending; }, 1);
_dns_lookup_pending = false;
if(aResult.isSet()) {
err = ERR_OK;

ip_addr_t addr;
auto pending = std::make_unique<_dns_found_result>(
_dns_found_result{
.addr = IPADDR_NONE,
.done = false,
});

err_t err = dns_gethostbyname_addrtype(aHostname,
&addr, &_dns_found_callback, pending.get(),
static_cast<uint8_t>(resolveType));

switch (err) {
// Address already known
case ERR_OK:
aResult = addr;
break;

// We are no longer able to issue requests
case ERR_MEM:
break;

// We need to wait for c/b to fire *or* we exit on our own timeout
// (which also requires us to notify the c/b that it is supposed to delete the pending obj)
case ERR_INPROGRESS:
// Re-check every 10ms, we expect this to happen fast
esp_delay(timeout_ms,
[&]() {
return !pending->done;
}, 10);

if (pending->done) {
if ((pending->addr).isSet()) {
aResult = pending->addr;
err = ERR_OK;
}
} else {
pending->done = true;
pending.release();
err = ERR_TIMEOUT;
}

break;
}

if(err == ERR_OK) {
if (err == ERR_OK) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str());
return 1;
}

DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %s (%d)!\n", aHostname, lwip_strerr(err), (int)err);

DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %s (%d)!\n",
aHostname,
(err == ERR_TIMEOUT) ? "Timeout" :
(err == ERR_INPROGRESS) ? "No response" :
"Unknown", static_cast<int>(err));

return 0;
}

#if LWIP_IPV4 && LWIP_IPV6
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms, DNSResolveType resolveType)
/**
* Resolve the given hostname to an IP address.
* @param aHostname Name to be resolved
* @param aResult IPAddress structure to store the returned IP address
* @return 1 if aIPAddrString was successfully converted to an IP address,
* else 0
*/
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
{
ip_addr_t addr;
err_t err;
aResult = static_cast<uint32_t>(INADDR_NONE);

if(aResult.fromString(aHostname)) {
// Host name is a IP address use it!
DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname);
return 1;
}

DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname);
switch(resolveType)
{
// Use selected addrtype
case DNSResolveType::DNS_AddrType_IPv4:
case DNSResolveType::DNS_AddrType_IPv6:
case DNSResolveType::DNS_AddrType_IPv4_IPv6:
case DNSResolveType::DNS_AddrType_IPv6_IPv4:
err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult, (uint8_t) resolveType);
break;
default:
err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult, LWIP_DNS_ADDRTYPE_DEFAULT); // If illegal type, use default.
break;
}

if(err == ERR_OK) {
aResult = IPAddress(&addr);
} else if(err == ERR_INPROGRESS) {
_dns_lookup_pending = true;
// will resume on timeout or when wifi_dns_found_callback fires
esp_delay(timeout_ms, []() { return _dns_lookup_pending; });
_dns_lookup_pending = false;
// will return here when dns_found_callback fires
if(aResult.isSet()) {
err = ERR_OK;
}
}
return hostByNameImpl(aHostname, aResult, DNSDefaultTimeoutMs, DNSResolveTypeDefault);
}

if(err == ERR_OK) {
DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str());
return 1;
}
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms)
{
return hostByNameImpl(aHostname, aResult, timeout_ms, DNSResolveTypeDefault);
}

DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err);
return 0;
#if LWIP_IPV4 && LWIP_IPV6
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms, DNSResolveType resolveType)
{
return hostByNameImpl(aHostname, aResult, timeout_ms, resolveType);
}
#endif

Expand All @@ -710,16 +706,19 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul
* @param ipaddr
* @param callback_arg
*/
void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
static void _dns_found_callback(const char*, const ip_addr_t* ipaddr, void* arg)
{
(void) name;
if (!_dns_lookup_pending) {
auto result = reinterpret_cast<_dns_found_result*>(arg);
if (result->done) {
delete result;
return;
}
if(ipaddr) {
(*reinterpret_cast<IPAddress*>(callback_arg)) = IPAddress(ipaddr);

if (ipaddr) {
result->addr = IPAddress(ipaddr);
}
_dns_lookup_pending = false; // resume hostByName

result->done = true;
esp_schedule();
}

Expand Down
15 changes: 11 additions & 4 deletions libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#define ESP8266WIFIGENERIC_H_

#include "ESP8266WiFiType.h"

#include <IPAddress.h>
#include <lwip/dns.h>

#include <functional>
#include <memory>

Expand All @@ -44,12 +48,15 @@ typedef void (*WiFiEventCb)(WiFiEvent_t);

enum class DNSResolveType: uint8_t
{
DNS_AddrType_IPv4 = 0, // LWIP_DNS_ADDRTYPE_IPV4 = 0
DNS_AddrType_IPv6, // LWIP_DNS_ADDRTYPE_IPV6 = 1
DNS_AddrType_IPv4_IPv6, // LWIP_DNS_ADDRTYPE_IPV4_IPV6 = 2
DNS_AddrType_IPv6_IPv4 // LWIP_DNS_ADDRTYPE_IPV6_IPV4 = 3
DNS_AddrType_IPv4 = LWIP_DNS_ADDRTYPE_IPV4,
DNS_AddrType_IPv6 = LWIP_DNS_ADDRTYPE_IPV6,
DNS_AddrType_IPv4_IPv6 = LWIP_DNS_ADDRTYPE_IPV4_IPV6,
DNS_AddrType_IPv6_IPv4 = LWIP_DNS_ADDRTYPE_IPV6_IPV4,
};

inline constexpr auto DNSDefaultTimeoutMs = 10000;
inline constexpr auto DNSResolveTypeDefault = static_cast<DNSResolveType>(LWIP_DNS_ADDRTYPE_DEFAULT);

struct WiFiState;

class ESP8266WiFiGenericClass {
Expand Down
21 changes: 15 additions & 6 deletions tests/host/common/ClientContextTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,27 @@

#include <netdb.h> // gethostbyname

err_t dns_gethostbyname(const char* hostname, ip_addr_t* addr, dns_found_callback found,
void* callback_arg)
err_t dns_gethostbyname_addrtype(const char* hostname, ip_addr_t* addr, dns_found_callback, void*,
u8 type)
{
(void)callback_arg;
(void)found;
struct hostent* hbn = gethostbyname(hostname);
auto* hbn = gethostbyname(hostname);
if (!hbn)
return ERR_TIMEOUT;
addr->addr = *(uint32_t*)hbn->h_addr_list[0];

uint32_t tmp;
std::memcpy(&tmp, hbn->h_addr_list[0], sizeof(tmp));
addr->addr = tmp;

return ERR_OK;
}

err_t dns_gethostbyname_addrtype(const char* hostname, ip_addr_t* addr, dns_found_callback found,
void* callback_arg)
{
return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg,
LWIP_DNS_ADDRTYPE_DEFAULT);
}

static struct tcp_pcb mock_tcp_pcb;
tcp_pcb* tcp_new(void)
{
Expand Down