Skip to content

Commit 5daade1

Browse files
committed
DNSServer use default settings for catch-all setup
- default constructor and start() method simply runs a catch-all DNS setup - avoid string comparison for domain reqs in catch-all mode - use IPaddress class for _resolvedIP (looking for IPv6 support in future)
1 parent 545a946 commit 5daade1

File tree

2 files changed

+60
-24
lines changed

2 files changed

+60
-24
lines changed

libraries/DNSServer/src/DNSServer.cpp

+36-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "DNSServer.h"
22
#include <lwip/def.h>
33
#include <Arduino.h>
4+
#include <WiFi.h>
5+
46

57
// #define DEBUG_ESP_DNS
68
#ifdef DEBUG_ESP_PORT
@@ -9,20 +11,34 @@
911
#define DEBUG_OUTPUT Serial
1012
#endif
1113

12-
DNSServer::DNSServer() : _port(0), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain){}
14+
#define DNS_MIN_REQ_LEN 17 // minimal size for DNS request asking ROOT = DNS_HEADER_SIZE + 1 null byte for Name + 4 bytes type/class
15+
16+
DNSServer::DNSServer() : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain){}
17+
18+
DNSServer::DNSServer(const String &domainName) : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain), _domainName(domainName){};
19+
20+
21+
bool DNSServer::start(){
22+
if (WiFi.getMode() & WIFI_AP){
23+
_resolvedIP = WiFi.softAPIP();
24+
} else return false; // won't run if WiFi is not in AP mode
1325

14-
DNSServer::~DNSServer(){}
26+
_udp.close();
27+
_udp.onPacket([this](AsyncUDPPacket& pkt){ this->_handleUDP(pkt); });
28+
return _udp.listen(_port);
29+
}
1530

1631
bool DNSServer::start(const uint16_t &port, const String &domainName,
1732
const IPAddress &resolvedIP)
1833
{
1934
_port = port;
20-
_domainName = domainName;
21-
_resolvedIP[0] = resolvedIP[0];
22-
_resolvedIP[1] = resolvedIP[1];
23-
_resolvedIP[2] = resolvedIP[2];
24-
_resolvedIP[3] = resolvedIP[3];
25-
downcaseAndRemoveWwwPrefix(_domainName);
35+
if (domainName != "*"){
36+
_domainName = domainName;
37+
downcaseAndRemoveWwwPrefix(_domainName);
38+
} else
39+
_domainName.clear();
40+
41+
_resolvedIP = resolvedIP;
2642
_udp.close();
2743
_udp.onPacket([this](AsyncUDPPacket& pkt){ this->_handleUDP(pkt); });
2844
return _udp.listen(_port);
@@ -51,8 +67,7 @@ void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
5167

5268
void DNSServer::_handleUDP(AsyncUDPPacket& pkt)
5369
{
54-
size_t currentPacketSize = pkt.length();
55-
if (currentPacketSize < DNS_HEADER_SIZE) return;
70+
if (pkt.length() < DNS_MIN_REQ_LEN) return; // truncated packet or not a DNS req
5671

5772
// get DNS header (beginning of message)
5873
DNSHeader dnsHeader;
@@ -67,15 +82,15 @@ void DNSServer::_handleUDP(AsyncUDPPacket& pkt)
6782
// Each label contains a byte to describe its length and the label itself. The list of
6883
// labels terminates with a zero-valued byte. In "github.com", we have two labels "github" & "com"
6984
*/
70-
const char * enoflbls = strchr((const char*)pkt.data() + DNS_HEADER_SIZE, 0); // find end_of_label marker
85+
const char * enoflbls = strchr(reinterpret_cast<const char*>(pkt.data()) + DNS_HEADER_SIZE, 0); // find end_of_label marker
7186
++enoflbls; // advance after null terminator
7287
dnsQuestion.QName = pkt.data() + DNS_HEADER_SIZE; // we can reference labels from the request
7388
dnsQuestion.QNameLength = enoflbls - (char*)pkt.data() - DNS_HEADER_SIZE;
7489
/*
7590
check if we aint going out of pkt bounds
7691
proper dns req should have label terminator at least 4 bytes before end of packet
7792
*/
78-
if (dnsQuestion.QNameLength < 3 || dnsQuestion.QNameLength > currentPacketSize - DNS_HEADER_SIZE - sizeof(dnsQuestion.QType) - sizeof(dnsQuestion.QClass)) return; // malformed packet
93+
if (dnsQuestion.QNameLength > pkt.length() - DNS_HEADER_SIZE - sizeof(dnsQuestion.QType) - sizeof(dnsQuestion.QClass)) return; // malformed packet
7994

8095
// Copy the QType and QClass
8196
memcpy( &dnsQuestion.QType, enoflbls, sizeof(dnsQuestion.QType) );
@@ -85,8 +100,8 @@ void DNSServer::_handleUDP(AsyncUDPPacket& pkt)
85100
// will reply with IP only to "*" or if doman matches without www. subdomain
86101
if (dnsHeader.OPCode == DNS_OPCODE_QUERY &&
87102
requestIncludesOnlyOneQuestion(dnsHeader) &&
88-
(_domainName == "*" ||
89-
getDomainNameWithoutWwwPrefix((const char*)pkt.data() + DNS_HEADER_SIZE, dnsQuestion.QNameLength) == _domainName)
103+
(_domainName.isEmpty() ||
104+
getDomainNameWithoutWwwPrefix(static_cast<const unsigned char*>(dnsQuestion.QName), dnsQuestion.QNameLength) == _domainName)
90105
)
91106
{
92107
replyWithIP(pkt, dnsHeader, dnsQuestion);
@@ -106,14 +121,14 @@ bool DNSServer::requestIncludesOnlyOneQuestion(DNSHeader& dnsHeader)
106121
}
107122

108123

109-
String DNSServer::getDomainNameWithoutWwwPrefix(const char* start, size_t len)
124+
String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char* start, size_t len)
110125
{
111126
String parsedDomainName(start, --len); // exclude trailing null byte from labels length, String constructor will add it anyway
112127

113128
int pos = 0;
114129
while(pos<len)
115130
{
116-
parsedDomainName.setCharAt(pos, 0x2e); // replace len byte with dot char "."
131+
parsedDomainName.setCharAt(pos, 0x2e); // replace label len byte with dot char "."
117132
pos += *(start + pos);
118133
++pos;
119134
}
@@ -151,23 +166,24 @@ void DNSServer::replyWithIP(AsyncUDPPacket& req, DNSHeader& dnsHeader, DNSQuesti
151166
rpl.write((unsigned char*) &answerClass, 2 );
152167
rpl.write((unsigned char*) &_ttl, 4); // DNS Time To Live
153168
rpl.write((unsigned char*) &answerIPv4, 2 );
154-
rpl.write(_resolvedIP, sizeof(_resolvedIP)); // The IP address to return
169+
uint32_t ip = _resolvedIP;
170+
rpl.write(reinterpret_cast<uint8_t*>(&ip), sizeof(uint32_t)); // The IPv4 address to return
155171

156172
_udp.sendTo(rpl, req.remoteIP(), req.remotePort());
157173

158174
#ifdef DEBUG_ESP_DNS
159175
DEBUG_OUTPUT.printf("DNS responds: %s for %s\n",
160-
IPAddress(_resolvedIP).toString().c_str(), getDomainNameWithoutWwwPrefix((const char*)rpl.data() + DNS_HEADER_SIZE, dnsQuestion.QNameLength).c_str() );
176+
_resolvedIP.toString().c_str(), getDomainNameWithoutWwwPrefix(static_cast<const unsigned char*>(dnsQuestion.QName), dnsQuestion.QNameLength).c_str() );
161177
#endif
162178
}
163179

164180
void DNSServer::replyWithCustomCode(AsyncUDPPacket& req, DNSHeader& dnsHeader)
165181
{
166182
dnsHeader.QR = DNS_QR_RESPONSE;
167-
dnsHeader.RCode = (uint16_t)_errorReplyCode;
183+
dnsHeader.RCode = static_cast<uint16_t>(_errorReplyCode);
168184
dnsHeader.QDCount = 0;
169185

170186
AsyncUDPMessage rpl(sizeof(DNSHeader));
171-
rpl.write((unsigned char*)&dnsHeader, sizeof(DNSHeader));
187+
rpl.write(reinterpret_cast<const uint8_t*>(&dnsHeader), sizeof(DNSHeader));
172188
_udp.sendTo(rpl, req.remoteIP(), req.remotePort());
173189
}

libraries/DNSServer/src/DNSServer.h

+24-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
#define DNS_QR_RESPONSE 1
66
#define DNS_OPCODE_QUERY 0
77
#define DNS_DEFAULT_TTL 60 // Default Time To Live : time interval in seconds that the resource record should be cached before being discarded
8-
#define DNS_OFFSET_DOMAIN_NAME 12 // Offset in bytes to reach the domain name in the DNS message
98
#define DNS_HEADER_SIZE 12
9+
#define DNS_OFFSET_DOMAIN_NAME DNS_HEADER_SIZE // Offset in bytes to reach the domain name labels in the DNS message
10+
#define DNS_DEFAULT_PORT 53
1011

1112
enum class DNSReplyCode:uint16_t
1213
{
@@ -75,11 +76,30 @@ class DNSServer
7576
{
7677
public:
7778
DNSServer();
78-
~DNSServer();
79+
/**
80+
* @brief Construct a new DNSServer object
81+
* builds DNS server with default parameters
82+
* @param domainName - domain name to serve
83+
*/
84+
DNSServer(const String &domainName);
85+
~DNSServer(){}; // default d-tor
7986
void processNextRequest(){}; // stub, left for compatibility with an old version
8087
void setErrorReplyCode(const DNSReplyCode &replyCode);
8188
void setTTL(const uint32_t &ttl);
8289

90+
/**
91+
* @brief Starts a server with current configuration or with default parameters
92+
* if it's the first call.
93+
* Defaults are:
94+
* port: 53
95+
* domainName: any
96+
* ip: WiFi AP's IP address
97+
*
98+
* @return true on success
99+
* @return false if IP or socket error
100+
*/
101+
bool start();
102+
83103
// Returns true if successful, false if there are no sockets available
84104
bool start(const uint16_t &port,
85105
const String &domainName,
@@ -93,7 +113,7 @@ class DNSServer
93113
uint32_t _ttl;
94114
DNSReplyCode _errorReplyCode;
95115
String _domainName;
96-
unsigned char _resolvedIP[4];
116+
IPAddress _resolvedIP;
97117

98118

99119
void downcaseAndRemoveWwwPrefix(String &domainName);
@@ -106,7 +126,7 @@ class DNSServer
106126
* @param len labels length
107127
* @return String
108128
*/
109-
String getDomainNameWithoutWwwPrefix(const char* start, size_t len);
129+
String getDomainNameWithoutWwwPrefix(const unsigned char* start, size_t len);
110130
inline bool requestIncludesOnlyOneQuestion(DNSHeader& dnsHeader);
111131
void replyWithIP(AsyncUDPPacket& req, DNSHeader& dnsHeader, DNSQuestion& dnsQuestion);
112132
inline void replyWithCustomCode(AsyncUDPPacket& req, DNSHeader& dnsHeader);

0 commit comments

Comments
 (0)