From c988ba16451186c55873793978e7730619a25074 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 19 Jun 2022 17:57:43 +0200 Subject: [PATCH] Add user control over DNS timeout reducing blocking Add command ``DnsTimeout 100..20000`` to change default DNS timeout from 1000 msec blocking if no DNS server found --- CHANGELOG.md | 4 +- RELEASENOTES.md | 3 +- lib/default/DnsClient/library.properties | 7 + lib/default/DnsClient/src/DnsClient.cpp | 337 ++++++++++++++++++++ lib/default/DnsClient/src/DnsClient.h | 42 +++ tasmota/include/i18n.h | 1 + tasmota/include/tasmota_globals.h | 4 + tasmota/include/tasmota_types.h | 3 +- tasmota/include/tasmota_version.h | 2 +- tasmota/my_user_config.h | 1 + tasmota/tasmota.ino | 3 + tasmota/tasmota_support/settings.ino | 4 + tasmota/tasmota_support/support_command.ino | 13 +- tasmota/tasmota_support/support_wifi.ino | 18 +- 14 files changed, 423 insertions(+), 19 deletions(-) create mode 100644 lib/default/DnsClient/library.properties create mode 100644 lib/default/DnsClient/src/DnsClient.cpp create mode 100644 lib/default/DnsClient/src/DnsClient.h diff --git a/CHANGELOG.md b/CHANGELOG.md index c74101723..c48a6fdbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development -## [12.0.1.1] +## [12.0.1.2] ### Added - +- Command ``DnsTimeout 100..20000`` to change default DNS timeout from 1000 msec blocking if no DNS server found ### Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2a55cad16..ec173e3d2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -107,8 +107,9 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v12.0.1.1 +## Changelog v12.0.1.2 ### Added +- Command ``DnsTimeout 100..20000`` to change default DNS timeout from 1000 msec blocking if no DNS server found ### Breaking Changed diff --git a/lib/default/DnsClient/library.properties b/lib/default/DnsClient/library.properties new file mode 100644 index 000000000..7ac2d2393 --- /dev/null +++ b/lib/default/DnsClient/library.properties @@ -0,0 +1,7 @@ +name=DnsClient +version=1.0 +author=MCQN Ltd, Theo Arends +maintainer=Theo +sentence=Dns client allowing timeout selection. +paragraph=This class uses WifiUdp. +architectures=esp8266,esp32 diff --git a/lib/default/DnsClient/src/DnsClient.cpp b/lib/default/DnsClient/src/DnsClient.cpp new file mode 100644 index 000000000..e46ff43e1 --- /dev/null +++ b/lib/default/DnsClient/src/DnsClient.cpp @@ -0,0 +1,337 @@ +/* + DnsClient.cpp - DNS client for Arduino + + SPDX-FileCopyrightText: 2009-2010 MCQN Ltd. and Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#include "DnsClient.h" + +// Various flags and header field values for a DNS message +#define UDP_HEADER_SIZE 8 +#define DNS_HEADER_SIZE 12 +#define TTL_SIZE 4 +#define QUERY_FLAG (0) +#define RESPONSE_FLAG (1<<15) +#define QUERY_RESPONSE_MASK (1<<15) +#define OPCODE_STANDARD_QUERY (0) +#define OPCODE_INVERSE_QUERY (1<<11) +#define OPCODE_STATUS_REQUEST (2<<11) +#define OPCODE_MASK (15<<11) +#define AUTHORITATIVE_FLAG (1<<10) +#define TRUNCATION_FLAG (1<<9) +#define RECURSION_DESIRED_FLAG (1<<8) +#define RECURSION_AVAILABLE_FLAG (1<<7) +#define RESP_NO_ERROR (0) +#define RESP_FORMAT_ERROR (1) +#define RESP_SERVER_FAILURE (2) +#define RESP_NAME_ERROR (3) +#define RESP_NOT_IMPLEMENTED (4) +#define RESP_REFUSED (5) +#define RESP_MASK (15) +#define TYPE_A (0x0001) +#define CLASS_IN (0x0001) +#define LABEL_COMPRESSION_MASK (0xC0) +// Port number that DNS servers listen on +#define DNS_PORT 53 + +// Possible return codes from ProcessResponse +#define SUCCESS 1 +#define TIMED_OUT -1 +#define INVALID_SERVER -2 +#define TRUNCATED -3 +#define INVALID_RESPONSE -4 + +#ifndef htons +#define htons(x) ( ((x)<< 8 & 0xFF00) | ((x)>> 8 & 0x00FF) ) +#endif + + +void DNSClient::begin(const IPAddress& aDNSServer) { + iDNSServer = aDNSServer; + iRequestId = 0; +} + +void DNSClient::setTimeout(uint16_t aTimeout) { + iTimeout = aTimeout; +} + +int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) { + int ret =0; + + // See if it's a numeric IP address + if (aResult.fromString(aHostname)) { + // It is, our work here is done + return 1; + } + + // Check we've got a valid DNS server to use + if ((0xFFFFFFFF == (uint32_t)iDNSServer) || (0 == (uint32_t)iDNSServer)) { + return INVALID_SERVER; + } + + // Find a socket to use + if (iUdp.begin(1024+(millis() & 0xF)) == 1) { + // Try up to three times + int retries = 0; +// while ((retries < 3) && (ret <= 0)) { + // Send DNS request + ret = iUdp.beginPacket(iDNSServer, DNS_PORT); + if (ret != 0) { + // Now output the request data + ret = BuildRequest(aHostname); + if (ret != 0) { + // And finally send the request + ret = iUdp.endPacket(); + if (ret != 0) { + // Now wait for a response + int wait_retries = 0; + ret = TIMED_OUT; + while ((wait_retries < 3) && (ret == TIMED_OUT)) { + ret = ProcessResponse(iTimeout, aResult); + wait_retries++; + } + } + } + } + retries++; +// } + // We're done with the socket now + iUdp.stop(); + } + return ret; +} + +uint16_t DNSClient::BuildRequest(const char* aName) { + // Build header + // 1 1 1 1 1 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | ID | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | QDCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | ANCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | NSCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | ARCOUNT | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // As we only support one request at a time at present, we can simplify + // some of this header + iRequestId = millis(); // generate a random ID + uint16_t twoByteBuffer; + + // FIXME We should also check that there's enough space available to write to, rather + // FIXME than assume there's enough space (as the code does at present) + iUdp.write((uint8_t*)&iRequestId, sizeof(iRequestId)); + + twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG); + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + twoByteBuffer = htons(1); // One question record + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + twoByteBuffer = 0; // Zero answer records + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + // and zero additional records + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + // Build question + const char* start =aName; + const char* end =start; + uint8_t len; + // Run through the name being requested + while (*end) { + // Find out how long this section of the name is + end = start; + while (*end && (*end != '.') ) { + end++; + } + + if (end-start > 0) { + // Write out the size of this section + len = end-start; + iUdp.write(&len, sizeof(len)); + // And then write out the section + iUdp.write((uint8_t*)start, end-start); + } + start = end+1; + } + + // We've got to the end of the question name, so terminate it with a zero-length section + len = 0; + iUdp.write(&len, sizeof(len)); + // Finally the type and class of question + twoByteBuffer = htons(TYPE_A); + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + + twoByteBuffer = htons(CLASS_IN); // Internet class of question + iUdp.write((uint8_t*)&twoByteBuffer, sizeof(twoByteBuffer)); + // Success! Everything buffered okay + return 1; +} + +uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) { + uint32_t startTime = millis(); + + // Wait for a response packet + while(iUdp.parsePacket() <= 0) { + if ((millis() - startTime) > aTimeout) { + return TIMED_OUT; + } + delay(20); + } + + // We've had a reply! + // Read the UDP header + uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header + // Check that it's a response from the right server and the right port + if ( (iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT) ) { + // It's not from who we expected + return INVALID_SERVER; + } + + // Read through the rest of the response + if (iUdp.available() < DNS_HEADER_SIZE) { + return TRUNCATED; + } + iUdp.read(header, DNS_HEADER_SIZE); + + uint16_t staging; // Staging used to avoid type-punning warnings + memcpy(&staging, &header[2], sizeof(uint16_t)); + uint16_t header_flags = htons(staging); + memcpy(&staging, &header[0], sizeof(uint16_t)); + // Check that it's a response to this request + if ( (iRequestId != staging) || ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t)RESPONSE_FLAG) ) { + // Mark the entire packet as read + iUdp.flush(); + return INVALID_RESPONSE; + } + // Check for any errors in the response (or in our request) + // although we don't do anything to get round these + if ( (header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK) ) { + // Mark the entire packet as read + iUdp.flush(); + return -5; // INVALID_RESPONSE; + } + + // And make sure we've got (at least) one answer + memcpy(&staging, &header[6], sizeof(uint16_t)); + uint16_t answerCount = htons(staging); + if (answerCount == 0 ) { + // Mark the entire packet as read + iUdp.flush(); + return -6; // INVALID_RESPONSE; + } + + // Skip over any questions + memcpy(&staging, &header[4], sizeof(uint16_t)); + for (uint16_t i =0; i < htons(staging); i++) { + // Skip over the name + uint8_t len; + do { + iUdp.read(&len, sizeof(len)); + if (len > 0) { + // Don't need to actually read the data out for the string, just + // advance ptr to beyond it + while(len--) { + iUdp.read(); // we don't care about the returned byte + } + } + } while (len != 0); + + // Now jump over the type and class + for (int i =0; i < 4; i++) { + iUdp.read(); // we don't care about the returned byte + } + } + + // Now we're up to the bit we're interested in, the answer + // There might be more than one answer (although we'll just use the first + // type A answer) and some authority and additional resource records but + // we're going to ignore all of them. + + for (uint16_t i =0; i < answerCount; i++) { + // Skip the name + uint8_t len; + do { + iUdp.read(&len, sizeof(len)); + if ((len & LABEL_COMPRESSION_MASK) == 0) { + // It's just a normal label + if (len > 0) { + // And it's got a length + // Don't need to actually read the data out for the string, + // just advance ptr to beyond it + while(len--) { + iUdp.read(); // we don't care about the returned byte + } + } + } else { + // This is a pointer to a somewhere else in the message for the + // rest of the name. We don't care about the name, and RFC1035 + // says that a name is either a sequence of labels ended with a + // 0 length octet or a pointer or a sequence of labels ending in + // a pointer. Either way, when we get here we're at the end of + // the name + // Skip over the pointer + iUdp.read(); // we don't care about the returned byte + // And set len so that we drop out of the name loop + len = 0; + } + } while (len != 0); + + // Check the type and class + uint16_t answerType; + uint16_t answerClass; + iUdp.read((uint8_t*)&answerType, sizeof(answerType)); + iUdp.read((uint8_t*)&answerClass, sizeof(answerClass)); + + // Ignore the Time-To-Live as we don't do any caching + for (int i =0; i < TTL_SIZE; i++) { + iUdp.read(); // We don't care about the returned byte + } + + // And read out the length of this answer + // Don't need header_flags anymore, so we can reuse it here + iUdp.read((uint8_t*)&header_flags, sizeof(header_flags)); + + if ( (htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN) ) { + if (htons(header_flags) != 4) { + // It's a weird size + // Mark the entire packet as read + iUdp.flush(); + return -9; // INVALID_RESPONSE; + } + iUdp.read(aAddress.raw_address(), 4); +// uint32_t address; +// iUdp.read((uint8_t*)&address, sizeof(address)); +// aAddress = (IPAddress)address; + + // Check we've got a valid address + if ((0xFFFFFFFF != (uint32_t)aAddress) && (0 != (uint32_t)aAddress)) { + return SUCCESS; + } + } else { + // This isn't an answer type we're after, move onto the next one + for (uint16_t i =0; i < htons(header_flags); i++) { + iUdp.read(); // we don't care about the returned byte + } + } + } + + // Mark the entire packet as read + iUdp.flush(); + + // If we get here then we haven't found an answer + return -10; // INVALID_RESPONSE; +} diff --git a/lib/default/DnsClient/src/DnsClient.h b/lib/default/DnsClient/src/DnsClient.h new file mode 100644 index 000000000..af1dfe42e --- /dev/null +++ b/lib/default/DnsClient/src/DnsClient.h @@ -0,0 +1,42 @@ +/* + DnsClient.h - DNS client for Arduino + + SPDX-FileCopyrightText: 2009-2010 MCQN Ltd. and Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#ifndef DNSClient_h +#define DNSClient_h + +#include +#include +#include + +class DNSClient { +public: + void begin(const IPAddress& aDNSServer); + void setTimeout(uint16_t aTimeout = 1000); + + /* Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, else error code + */ + int getHostByName(const char* aHostname, IPAddress& aResult); + +protected: + uint16_t BuildRequest(const char* aName); + uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); + + IPAddress iDNSServer; + uint16_t iRequestId; + uint16_t iTimeout = 1000; + WiFiUDP iUdp; +}; + +#endif diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index 473bd8b4a..87706b3b7 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -317,6 +317,7 @@ #define D_WCFG_5_WAIT "Wait" #define D_WCFG_6_SERIAL "Serial" #define D_WCFG_7_WIFIMANAGER_RESET_ONLY "ManagerRst" +#define D_CMND_DNSTIMEOUT "DnsTimeout" #define D_CMND_DEVICENAME "DeviceName" #define D_CMND_FRIENDLYNAME "FriendlyName" #define D_CMND_FN "FN" diff --git a/tasmota/include/tasmota_globals.h b/tasmota/include/tasmota_globals.h index fce339e63..abdb5deb2 100644 --- a/tasmota/include/tasmota_globals.h +++ b/tasmota/include/tasmota_globals.h @@ -268,6 +268,10 @@ String EthernetMacAddress(void); #define TASM_FILE_AUTOEXEC "/autoexec.bat" // Commands executed after restart #define TASM_FILE_CONFIG "/config.sys" // Settings executed after restart +#ifndef DNS_TIMEOUT +#define DNS_TIMEOUT 1000 // Milliseconds +#endif + #ifndef MQTT_MAX_PACKET_SIZE #define MQTT_MAX_PACKET_SIZE 1200 // Bytes //#define MQTT_MAX_PACKET_SIZE 2048 // Bytes diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index a501d90d0..2aaa9aaf6 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -675,8 +675,9 @@ typedef struct { uint8_t knx_CB_registered; // 4A8 Number of Group Address to write uint8_t switchmode[MAX_SWITCHES_SET]; // 4A9 - uint8_t free_4c5[5]; // 4C5 + uint8_t free_4c5[3]; // 4C5 + uint16_t dns_timeout; // 4C8 uint8_t ds3502_state[MAX_DS3502]; // 4CA uint16_t influxdb_port; // 4CE power_t interlock[MAX_INTERLOCKS_SET]; // 4D0 MAX_INTERLOCKS = MAX_RELAYS / 2 diff --git a/tasmota/include/tasmota_version.h b/tasmota/include/tasmota_version.h index 3977366ca..2ff5a7c29 100644 --- a/tasmota/include/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x0C000101; // 12.0.1.1 +const uint32_t VERSION = 0x0C000102; // 12.0.1.2 #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index b8c9901a8..1ce6bf230 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -79,6 +79,7 @@ #define WIFI_CONFIG_TOOL WIFI_RETRY // [WifiConfig] Default tool if Wi-Fi fails to connect (default option: 4 - WIFI_RETRY) // (WIFI_RESTART, WIFI_MANAGER, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY) // The configuration can be changed after first setup using WifiConfig 0, 2, 4, 5, 6 and 7. +#define DNS_TIMEOUT 1000 // [DnsTimeout] Number of ms before DNS timeout #define WIFI_ARP_INTERVAL 60 // [SetOption41] Send gratuitous ARP interval #define WIFI_SCAN_AT_RESTART false // [SetOption56] Scan Wi-Fi network at restart for configured AP's #define WIFI_SCAN_REGULARLY true // [SetOption57] Scan Wi-Fi network every 44 minutes for configured AP's diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 77a63fb24..00d3a21d1 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -35,6 +35,7 @@ // Libraries #include // Ota #include // Ota +#include // Any getHostByName #ifdef ESP32 #ifdef USE_TLS #include "HTTPUpdateLight.h" // Ota over HTTPS for ESP32 @@ -158,6 +159,7 @@ struct XDRVMAILBOX { char *command; } XdrvMailbox; +DNSClient DnsClient; WiFiUDP PortUdp; // UDP Syslog and Alexa #ifdef ESP32 @@ -588,6 +590,7 @@ void setup(void) { TasmotaGlobal.init_state = INIT_GPIOS; SetPowerOnState(); + DnsClient.setTimeout(Settings->dns_timeout); WifiConnect(); AddLog(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s - %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE "(%s)"), diff --git a/tasmota/tasmota_support/settings.ino b/tasmota/tasmota_support/settings.ino index 74c59e0aa..35b9f9dbe 100644 --- a/tasmota/tasmota_support/settings.ino +++ b/tasmota/tasmota_support/settings.ino @@ -926,6 +926,7 @@ void SettingsDefaultSet2(void) { flag3.use_wifi_scan |= WIFI_SCAN_AT_RESTART; flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY; Settings->wifi_output_power = 170; + Settings->dns_timeout = DNS_TIMEOUT; Settings->param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL; ParseIPv4(&Settings->ipv4_address[0], PSTR(WIFI_IP_ADDRESS)); ParseIPv4(&Settings->ipv4_address[1], PSTR(WIFI_GATEWAY)); @@ -1540,6 +1541,9 @@ void SettingsDelta(void) { Settings->weight_offset = Settings->energy_frequency_calibration * Settings->weight_calibration; #endif } + if (Settings->version < 0x0C000102) { // 12.0.1.2 + Settings->dns_timeout = DNS_TIMEOUT; + } Settings->version = VERSION; SettingsSave(1); diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 8bf0ef967..584e35236 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -30,7 +30,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALBUFFER "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|" - D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" + D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DNSTIMEOUT "|" D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_SWITCHTEXT "|" @@ -65,7 +65,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialBuffer, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter, - &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, + &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout, &CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndSwitchText, @@ -2277,6 +2277,15 @@ void CmndWifi(void) Response_P(PSTR("{\"" D_JSON_WIFI "\":\"%s\",\"" D_JSON_WIFI_MODE "\":\"11%c\"}"), GetStateText(Settings->flag4.network_wifi), pgm_read_byte(&kWifiPhyMode[WiFi.getPhyMode() & 0x3]) ); } +void CmndDnsTimeout(void) { + // Set timeout between 100 and 20000 mSec + if ((XdrvMailbox.payload >= 100) && (XdrvMailbox.payload <= 20000)) { + Settings->dns_timeout = XdrvMailbox.payload; + DnsClient.setTimeout(Settings->dns_timeout); + } + ResponseCmndNumber(Settings->dns_timeout); +} + #ifdef USE_I2C void CmndI2cScan(void) { diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 075072c64..8d4d371e4 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -729,20 +729,14 @@ void wifiKeepAlive(void) { } #endif // ESP8266 -int WifiHostByName(const char* aHostname, IPAddress& aResult) { +bool WifiHostByName(const char* aHostname, IPAddress& aResult) { // Use this instead of WiFi.hostByName or connect(host_name,.. to block less if DNS server is not found - aResult = (uint32_t)(0); - if (aResult.fromString(aHostname)) { - // Host name is already an IP address so use it! - return 1; + uint32_t dns_address = (!TasmotaGlobal.global_state.eth_down) ? Settings->eth_ipv4_address[3] : Settings->ipv4_address[3]; + DnsClient.begin((IPAddress)dns_address); + if (1 == DnsClient.getHostByName(aHostname, aResult)) { + return true; } - else if (WiFi.hostByName(aHostname, aResult)) { - // Host name resolved - if (0xFFFFFFFF != (uint32_t)aResult) { - return 1; - } - } - return 0; + return false; } void WifiPollNtp() {