Skip to content

Commit 5c866c5

Browse files
Merge branch 'master' into wvftonmi
2 parents 1d2c939 + 7c2e071 commit 5c866c5

File tree

6 files changed

+261
-125
lines changed

6 files changed

+261
-125
lines changed

libraries/DNSServer/src/DNSServer.cpp

Lines changed: 169 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
#include "DNSServer.h"
22
#include <lwip/def.h>
33
#include <Arduino.h>
4+
#include <memory>
45

56
#ifdef DEBUG_ESP_PORT
67
#define DEBUG_OUTPUT DEBUG_ESP_PORT
78
#else
89
#define DEBUG_OUTPUT Serial
910
#endif
1011

12+
#define DNS_HEADER_SIZE sizeof(DNSHeader)
13+
1114
DNSServer::DNSServer()
1215
{
1316
_ttl = lwip_htonl(60);
@@ -46,149 +49,208 @@ void DNSServer::stop()
4649
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
4750
{
4851
domainName.toLowerCase();
49-
domainName.replace("www.", "");
52+
if (domainName.startsWith("www."))
53+
domainName.remove(0, 4);
5054
}
5155

52-
void DNSServer::processNextRequest()
56+
void DNSServer::respondToRequest(uint8_t *buffer, size_t length)
5357
{
54-
size_t packetSize = _udp.parsePacket();
58+
DNSHeader *dnsHeader;
59+
uint8_t *query, *start;
60+
const char *matchString;
61+
size_t remaining, labelLength, queryLength;
62+
uint16_t qtype, qclass;
63+
64+
dnsHeader = (DNSHeader *)buffer;
5565

56-
if (packetSize >= sizeof(DNSHeader))
57-
{
58-
uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(packetSize));
59-
if (buffer == NULL) return;
66+
// Must be a query for us to do anything with it
67+
if (dnsHeader->QR != DNS_QR_QUERY)
68+
return;
6069

61-
_udp.read(buffer, packetSize);
70+
// If operation is anything other than query, we don't do it
71+
if (dnsHeader->OPCode != DNS_OPCODE_QUERY)
72+
return replyWithError(dnsHeader, DNSReplyCode::NotImplemented);
73+
74+
// Only support requests containing single queries - everything else
75+
// is badly defined
76+
if (dnsHeader->QDCount != lwip_htons(1))
77+
return replyWithError(dnsHeader, DNSReplyCode::FormError);
78+
79+
// We must return a FormError in the case of a non-zero ARCount to
80+
// be minimally compatible with EDNS resolvers
81+
if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0
82+
|| dnsHeader->ARCount != 0)
83+
return replyWithError(dnsHeader, DNSReplyCode::FormError);
84+
85+
// Even if we're not going to use the query, we need to parse it
86+
// so we can check the address type that's being queried
87+
88+
query = start = buffer + DNS_HEADER_SIZE;
89+
remaining = length - DNS_HEADER_SIZE;
90+
while (remaining != 0 && *start != 0) {
91+
labelLength = *start;
92+
if (labelLength + 1 > remaining)
93+
return replyWithError(dnsHeader, DNSReplyCode::FormError);
94+
remaining -= (labelLength + 1);
95+
start += (labelLength + 1);
96+
}
6297

63-
DNSHeader* dnsHeader = reinterpret_cast<DNSHeader*>(buffer);
98+
// 1 octet labelLength, 2 octet qtype, 2 octet qclass
99+
if (remaining < 5)
100+
return replyWithError(dnsHeader, DNSReplyCode::FormError);
64101

65-
if (dnsHeader->QR == DNS_QR_QUERY &&
66-
dnsHeader->OPCode == DNS_OPCODE_QUERY &&
67-
requestIncludesOnlyOneQuestion(dnsHeader) &&
68-
(_domainName == "*" || getDomainNameWithoutWwwPrefix(buffer, packetSize) == _domainName)
69-
)
70-
{
71-
replyWithIP(buffer, packetSize);
72-
}
73-
else if (dnsHeader->QR == DNS_QR_QUERY)
74-
{
75-
replyWithCustomCode(buffer, packetSize);
102+
start += 1; // Skip the 0 length label that we found above
103+
104+
memcpy(&qtype, start, sizeof(qtype));
105+
start += 2;
106+
memcpy(&qclass, start, sizeof(qclass));
107+
start += 2;
108+
109+
queryLength = start - query;
110+
111+
if (qclass != lwip_htons(DNS_QCLASS_ANY)
112+
&& qclass != lwip_htons(DNS_QCLASS_IN))
113+
return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain,
114+
query, queryLength);
115+
116+
if (qtype != lwip_htons(DNS_QTYPE_A)
117+
&& qtype != lwip_htons(DNS_QTYPE_ANY))
118+
return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain,
119+
query, queryLength);
120+
121+
// If we have no domain name configured, just return an error
122+
if (_domainName == "")
123+
return replyWithError(dnsHeader, _errorReplyCode,
124+
query, queryLength);
125+
126+
// If we're running with a wildcard we can just return a result now
127+
if (_domainName == "*")
128+
return replyWithIP(dnsHeader, query, queryLength);
129+
130+
matchString = _domainName.c_str();
131+
132+
start = query;
133+
134+
// If there's a leading 'www', skip it
135+
if (*start == 3 && strncasecmp("www", (char *) start + 1, 3) == 0)
136+
start += 4;
137+
138+
while (*start != 0) {
139+
labelLength = *start;
140+
start += 1;
141+
while (labelLength > 0) {
142+
if (tolower(*start) != *matchString)
143+
return replyWithError(dnsHeader, _errorReplyCode,
144+
query, queryLength);
145+
++start;
146+
++matchString;
147+
--labelLength;
76148
}
149+
if (*start == 0 && *matchString == '\0')
150+
return replyWithIP(dnsHeader, query, queryLength);
77151

78-
free(buffer);
152+
if (*matchString != '.')
153+
return replyWithError(dnsHeader, _errorReplyCode,
154+
query, queryLength);
155+
++matchString;
79156
}
80-
}
81157

82-
bool DNSServer::requestIncludesOnlyOneQuestion(const DNSHeader* dnsHeader)
83-
{
84-
return lwip_ntohs(dnsHeader->QDCount) == 1 &&
85-
dnsHeader->ANCount == 0 &&
86-
dnsHeader->NSCount == 0 &&
87-
dnsHeader->ARCount == 0;
158+
return replyWithError(dnsHeader, _errorReplyCode,
159+
query, queryLength);
88160
}
89161

90-
String DNSServer::getDomainNameWithoutWwwPrefix(const uint8_t* buffer, size_t packetSize)
162+
void DNSServer::processNextRequest()
91163
{
92-
String parsedDomainName;
93-
94-
const uint8_t* pos = buffer + sizeof(DNSHeader);
95-
const uint8_t* end = buffer + packetSize;
96-
97-
// to minimize reallocations due to concats below
98-
// we reserve enough space that a median or average domain
99-
// name size cold be easily contained without a reallocation
100-
// - max size would be 253, in 2013, average is 11 and max was 42
101-
//
102-
parsedDomainName.reserve(32);
103-
104-
uint8_t labelLength = *pos;
105-
106-
while (true)
107-
{
108-
if (labelLength == 0)
109-
{
110-
// no more labels
111-
downcaseAndRemoveWwwPrefix(parsedDomainName);
112-
return parsedDomainName;
113-
}
164+
size_t currentPacketSize;
114165

115-
// append next label
116-
for (int i = 0; i < labelLength && pos < end; i++)
117-
{
118-
pos++;
119-
parsedDomainName += static_cast<char>(*pos);
120-
}
166+
currentPacketSize = _udp.parsePacket();
167+
if (currentPacketSize == 0)
168+
return;
121169

122-
if (pos >= end)
123-
{
124-
// malformed packet, return an empty domain name
125-
parsedDomainName = "";
126-
return parsedDomainName;
127-
}
128-
else
129-
{
130-
// next label
131-
pos++;
132-
labelLength = *pos;
133-
134-
// if there is another label, add delimiter
135-
if (labelLength != 0)
136-
{
137-
parsedDomainName += ".";
138-
}
139-
}
140-
}
170+
// The DNS RFC requires that DNS packets be less than 512 bytes in size,
171+
// so just discard them if they are larger
172+
if (currentPacketSize > MAX_DNS_PACKETSIZE)
173+
return;
174+
175+
// If the packet size is smaller than the DNS header, then someone is
176+
// messing with us
177+
if (currentPacketSize < DNS_HEADER_SIZE)
178+
return;
179+
180+
std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[currentPacketSize]);
181+
182+
if (buffer == NULL)
183+
return;
184+
185+
_udp.read(buffer.get(), currentPacketSize);
186+
respondToRequest(buffer.get(), currentPacketSize);
187+
}
188+
189+
void DNSServer::writeNBOShort(uint16_t value)
190+
{
191+
_udp.write((unsigned char *)&value, 2);
141192
}
142193

143-
void DNSServer::replyWithIP(uint8_t* buffer, size_t packetSize)
194+
void DNSServer::replyWithIP(DNSHeader *dnsHeader,
195+
unsigned char * query,
196+
size_t queryLength)
144197
{
145-
DNSHeader* dnsHeader = reinterpret_cast<DNSHeader*>(buffer);
198+
uint16_t value;
146199

147200
dnsHeader->QR = DNS_QR_RESPONSE;
148-
dnsHeader->ANCount = dnsHeader->QDCount;
149-
dnsHeader->QDCount = dnsHeader->QDCount;
150-
//dnsHeader->RA = 1;
201+
dnsHeader->QDCount = lwip_htons(1);
202+
dnsHeader->ANCount = lwip_htons(1);
203+
dnsHeader->NSCount = 0;
204+
dnsHeader->ARCount = 0;
151205

152206
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
153-
_udp.write(buffer, packetSize);
207+
_udp.write((unsigned char *) dnsHeader, sizeof(DNSHeader));
208+
_udp.write(query, queryLength);
209+
210+
// Rather than restate the name here, we use a pointer to the name contained
211+
// in the query section. Pointers have the top two bits set.
212+
value = 0xC000 | DNS_HEADER_SIZE;
213+
writeNBOShort(lwip_htons(value));
154214

155-
_udp.write((uint8_t)192); // answer name is a pointer
156-
_udp.write((uint8_t)12); // pointer to offset at 0x00c
215+
// Answer is type A (an IPv4 address)
216+
writeNBOShort(lwip_htons(DNS_QTYPE_A));
157217

158-
_udp.write((uint8_t)0); // 0x0001 answer is type A query (host address)
159-
_udp.write((uint8_t)1);
218+
// Answer is in the Internet Class
219+
writeNBOShort(lwip_htons(DNS_QCLASS_IN));
160220

161-
_udp.write((uint8_t)0); //0x0001 answer is class IN (internet address)
162-
_udp.write((uint8_t)1);
163-
221+
// Output TTL (already NBO)
164222
_udp.write((unsigned char*)&_ttl, 4);
165223

166224
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
167-
_udp.write((uint8_t)0);
168-
_udp.write((uint8_t)4);
225+
writeNBOShort(lwip_htons(sizeof(_resolvedIP)));
169226
_udp.write(_resolvedIP, sizeof(_resolvedIP));
170227
_udp.endPacket();
171-
172-
#ifdef DEBUG_ESP_DNS
173-
DEBUG_OUTPUT.printf("DNS responds: %s for %s\n",
174-
IPAddress(_resolvedIP).toString().c_str(), getDomainNameWithoutWwwPrefix(buffer, packetSize).c_str() );
175-
#endif
176228
}
177229

178-
void DNSServer::replyWithCustomCode(uint8_t* buffer, size_t packetSize)
230+
void DNSServer::replyWithError(DNSHeader *dnsHeader,
231+
DNSReplyCode rcode,
232+
unsigned char *query,
233+
size_t queryLength)
179234
{
180-
if (packetSize < sizeof(DNSHeader))
181-
{
182-
return;
183-
}
184-
185-
DNSHeader* dnsHeader = reinterpret_cast<DNSHeader*>(buffer);
186-
187235
dnsHeader->QR = DNS_QR_RESPONSE;
188-
dnsHeader->RCode = (unsigned char)_errorReplyCode;
189-
dnsHeader->QDCount = 0;
236+
dnsHeader->RCode = (unsigned char) rcode;
237+
if (query)
238+
dnsHeader->QDCount = lwip_htons(1);
239+
else
240+
dnsHeader->QDCount = 0;
241+
dnsHeader->ANCount = 0;
242+
dnsHeader->NSCount = 0;
243+
dnsHeader->ARCount = 0;
190244

191245
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
192-
_udp.write(buffer, sizeof(DNSHeader));
246+
_udp.write((unsigned char *)dnsHeader, sizeof(DNSHeader));
247+
if (query != NULL)
248+
_udp.write(query, queryLength);
193249
_udp.endPacket();
194250
}
251+
252+
void DNSServer::replyWithError(DNSHeader *dnsHeader,
253+
DNSReplyCode rcode)
254+
{
255+
replyWithError(dnsHeader, rcode, NULL, 0);
256+
}

libraries/DNSServer/src/DNSServer.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
#define DNS_QR_RESPONSE 1
77
#define DNS_OPCODE_QUERY 0
88

9+
#define DNS_QCLASS_IN 1
10+
#define DNS_QCLASS_ANY 255
11+
12+
#define DNS_QTYPE_A 1
13+
#define DNS_QTYPE_ANY 255
14+
915
#define MAX_DNSNAME_LENGTH 253
16+
#define MAX_DNS_PACKETSIZE 512
1017

1118
enum class DNSReplyCode
1219
{
@@ -65,9 +72,16 @@ class DNSServer
6572
DNSReplyCode _errorReplyCode;
6673

6774
void downcaseAndRemoveWwwPrefix(String &domainName);
68-
String getDomainNameWithoutWwwPrefix(const uint8_t* buffer, size_t packetSize);
69-
bool requestIncludesOnlyOneQuestion(const DNSHeader* dnsHeader);
70-
void replyWithIP(uint8_t* buffer, size_t packetSize);
71-
void replyWithCustomCode(uint8_t* buffer, size_t packetSize);
75+
void replyWithIP(DNSHeader *dnsHeader,
76+
unsigned char * query,
77+
size_t queryLength);
78+
void replyWithError(DNSHeader *dnsHeader,
79+
DNSReplyCode rcode,
80+
unsigned char *query,
81+
size_t queryLength);
82+
void replyWithError(DNSHeader *dnsHeader,
83+
DNSReplyCode rcode);
84+
void respondToRequest(uint8_t *buffer, size_t length);
85+
void writeNBOShort(uint16_t value);
7286
};
73-
#endif
87+
#endif

0 commit comments

Comments
 (0)