Skip to content

Commit 694c3a4

Browse files
LaurentLoufme-no-dev
authored andcommitted
DNS Server : bug fix and prettifying (#1011)
* Retrieve some code from what has been done on the ESP8266. Clarify a bit the signification of several bytes in the response. * Add the type and class as members of the DNS class for an eventual future use. * Clarify the sense of a magic number present in DNS server. * A bit of aesthetics for the DNS server. * Add a structure for the DNS question, use it DNS server to store the question data and to create the DNS answer from scratch.
1 parent d29cfdb commit 694c3a4

File tree

2 files changed

+109
-24
lines changed

2 files changed

+109
-24
lines changed

libraries/DNSServer/src/DNSServer.cpp

+69-17
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55

66
DNSServer::DNSServer()
77
{
8-
_ttl = htonl(60);
8+
_ttl = htonl(DNS_DEFAULT_TTL);
99
_errorReplyCode = DNSReplyCode::NonExistentDomain;
10-
_dnsHeader = NULL;
11-
_buffer = NULL;
10+
_dnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ;
11+
_dnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ;
12+
_buffer = NULL;
1213
_currentPacketSize = 0;
1314
_port = 0;
1415
}
@@ -55,11 +56,37 @@ void DNSServer::processNextRequest()
5556
_currentPacketSize = _udp.parsePacket();
5657
if (_currentPacketSize)
5758
{
58-
if (_buffer != NULL) free(_buffer);
59+
// Allocate buffer for the DNS query
60+
if (_buffer != NULL)
61+
free(_buffer);
5962
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
60-
if (_buffer == NULL) return;
63+
if (_buffer == NULL)
64+
return;
65+
66+
// Put the packet received in the buffer and get DNS header (beginning of message)
67+
// and the question
6168
_udp.read(_buffer, _currentPacketSize);
62-
_dnsHeader = (DNSHeader*) _buffer;
69+
memcpy( _dnsHeader, _buffer, DNS_HEADER_SIZE ) ;
70+
if ( requestIncludesOnlyOneQuestion() )
71+
{
72+
// The QName has a variable length, maximum 255 bytes and is comprised of multiple labels.
73+
// Each label contains a byte to describe its length and the label itself. The list of
74+
// labels terminates with a zero-valued byte. In "github.com", we have two labels "github" & "com"
75+
// Iterate through the labels and copy them as they come into a single buffer (for simplicity's sake)
76+
_dnsQuestion->QNameLength = 0 ;
77+
while ( _buffer[ DNS_HEADER_SIZE + _dnsQuestion->QNameLength ] != 0 )
78+
{
79+
memcpy( (void*) &_dnsQuestion->QName[_dnsQuestion->QNameLength], (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ) ;
80+
_dnsQuestion->QNameLength += _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ;
81+
}
82+
_dnsQuestion->QName[_dnsQuestion->QNameLength] = 0 ;
83+
_dnsQuestion->QNameLength++ ;
84+
85+
// Copy the QType and QClass
86+
memcpy( &_dnsQuestion->QType, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], sizeof(_dnsQuestion->QType) ) ;
87+
memcpy( &_dnsQuestion->QClass, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength + sizeof(_dnsQuestion->QType)], sizeof(_dnsQuestion->QClass) ) ;
88+
}
89+
6390

6491
if (_dnsHeader->QR == DNS_QR_QUERY &&
6592
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
@@ -87,15 +114,21 @@ bool DNSServer::requestIncludesOnlyOneQuestion()
87114
_dnsHeader->ARCount == 0;
88115
}
89116

117+
90118
String DNSServer::getDomainNameWithoutWwwPrefix()
91119
{
120+
// Error checking : if the buffer containing the DNS request is a null pointer, return an empty domain
92121
String parsedDomainName = "";
93-
if (_buffer == NULL) return parsedDomainName;
94-
unsigned char *start = _buffer + 12;
122+
if (_buffer == NULL)
123+
return parsedDomainName;
124+
125+
// Set the start of the domain just after the header (12 bytes). If equal to null character, return an empty domain
126+
unsigned char *start = _buffer + DNS_OFFSET_DOMAIN_NAME;
95127
if (*start == 0)
96128
{
97129
return parsedDomainName;
98130
}
131+
99132
int pos = 0;
100133
while(true)
101134
{
@@ -121,16 +154,35 @@ String DNSServer::getDomainNameWithoutWwwPrefix()
121154
void DNSServer::replyWithIP()
122155
{
123156
if (_buffer == NULL) return;
124-
_dnsHeader->QR = DNS_QR_RESPONSE;
125-
_dnsHeader->ANCount = _dnsHeader->QDCount;
126-
_dnsHeader->QDCount = 0;
127-
157+
128158
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
129-
_udp.write(_buffer, _currentPacketSize);
130-
_udp.write((unsigned char*)&_ttl, 4);
131-
_udp.write((uint8_t)0);
132-
_udp.write((uint8_t)4);
133-
_udp.write(_resolvedIP, 4);
159+
160+
// Change the type of message to a response and set the number of answers equal to
161+
// the number of questions in the header
162+
_dnsHeader->QR = DNS_QR_RESPONSE;
163+
_dnsHeader->ANCount = _dnsHeader->QDCount;
164+
_udp.write( (unsigned char*) _dnsHeader, DNS_HEADER_SIZE ) ;
165+
166+
// Write the question
167+
_udp.write(_dnsQuestion->QName, _dnsQuestion->QNameLength) ;
168+
_udp.write( (unsigned char*) &_dnsQuestion->QType, 2 ) ;
169+
_udp.write( (unsigned char*) &_dnsQuestion->QClass, 2 ) ;
170+
171+
// Write the answer
172+
// Use DNS name compression : instead of repeating the name in this RNAME occurence,
173+
// set the two MSB of the byte corresponding normally to the length to 1. The following
174+
// 14 bits must be used to specify the offset of the domain name in the message
175+
// (<255 here so the first byte has the 6 LSB at 0)
176+
_udp.write((uint8_t) 0xC0);
177+
_udp.write((uint8_t) DNS_OFFSET_DOMAIN_NAME);
178+
179+
// DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
180+
uint16_t answerType = htons(DNS_TYPE_A), answerClass = htons(DNS_CLASS_IN), answerIPv4 = htons(DNS_RDLENGTH_IPV4) ;
181+
_udp.write((unsigned char*) &answerType, 2 );
182+
_udp.write((unsigned char*) &answerClass, 2 );
183+
_udp.write((unsigned char*) &_ttl, 4); // DNS Time To Live
184+
_udp.write((unsigned char*) &answerIPv4, 2 );
185+
_udp.write(_resolvedIP, sizeof(_resolvedIP)); // The IP address to return
134186
_udp.endPacket();
135187
}
136188

libraries/DNSServer/src/DNSServer.h

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

912
enum class DNSReplyCode
1013
{
11-
NoError = 0,
14+
NoError = 0,
1215
FormError = 1,
13-
ServerFailure = 2,
16+
ServerFailure = 2,
1417
NonExistentDomain = 3,
15-
NotImplemented = 4,
16-
Refused = 5,
17-
YXDomain = 6,
18-
YXRRSet = 7,
19-
NXRRSet = 8
18+
NotImplemented = 4,
19+
Refused = 5,
20+
YXDomain = 6,
21+
YXRRSet = 7,
22+
NXRRSet = 8
2023
};
2124

25+
enum DNSType
26+
{
27+
DNS_TYPE_A = 1, // Host Address
28+
DNS_TYPE_AAAA = 28, // IPv6 Address
29+
DNS_TYPE_SOA = 6, // Start Of a zone of Authority
30+
DNS_TYPE_PTR = 12, // Domain name PoinTeR
31+
DNS_TYPE_DNAME = 39 // Delegation Name
32+
} ;
33+
34+
enum DNSClass
35+
{
36+
DNS_CLASS_IN = 1, // INternet
37+
DNS_CLASS_CH = 3 // CHaos
38+
} ;
39+
40+
enum DNSRDLength
41+
{
42+
DNS_RDLENGTH_IPV4 = 4 // 4 bytes for an IPv4 address
43+
} ;
44+
2245
struct DNSHeader
2346
{
2447
uint16_t ID; // identification number
@@ -41,6 +64,14 @@ struct DNSHeader
4164
uint16_t ARCount; // number of resource entries
4265
};
4366

67+
struct DNSQuestion
68+
{
69+
uint8_t QName[255] ;
70+
int8_t QNameLength ;
71+
uint16_t QType ;
72+
uint16_t QClass ;
73+
} ;
74+
4475
class DNSServer
4576
{
4677
public:
@@ -66,6 +97,8 @@ class DNSServer
6697
DNSHeader* _dnsHeader;
6798
uint32_t _ttl;
6899
DNSReplyCode _errorReplyCode;
100+
DNSQuestion* _dnsQuestion ;
101+
69102

70103
void downcaseAndRemoveWwwPrefix(String &domainName);
71104
String getDomainNameWithoutWwwPrefix();

0 commit comments

Comments
 (0)