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.
This commit is contained in:
parent
d29cfdb104
commit
694c3a453f
@ -5,9 +5,10 @@
|
|||||||
|
|
||||||
DNSServer::DNSServer()
|
DNSServer::DNSServer()
|
||||||
{
|
{
|
||||||
_ttl = htonl(60);
|
_ttl = htonl(DNS_DEFAULT_TTL);
|
||||||
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
||||||
_dnsHeader = NULL;
|
_dnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ;
|
||||||
|
_dnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ;
|
||||||
_buffer = NULL;
|
_buffer = NULL;
|
||||||
_currentPacketSize = 0;
|
_currentPacketSize = 0;
|
||||||
_port = 0;
|
_port = 0;
|
||||||
@ -55,11 +56,37 @@ void DNSServer::processNextRequest()
|
|||||||
_currentPacketSize = _udp.parsePacket();
|
_currentPacketSize = _udp.parsePacket();
|
||||||
if (_currentPacketSize)
|
if (_currentPacketSize)
|
||||||
{
|
{
|
||||||
if (_buffer != NULL) free(_buffer);
|
// Allocate buffer for the DNS query
|
||||||
|
if (_buffer != NULL)
|
||||||
|
free(_buffer);
|
||||||
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
|
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
|
||||||
if (_buffer == NULL) return;
|
if (_buffer == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Put the packet received in the buffer and get DNS header (beginning of message)
|
||||||
|
// and the question
|
||||||
_udp.read(_buffer, _currentPacketSize);
|
_udp.read(_buffer, _currentPacketSize);
|
||||||
_dnsHeader = (DNSHeader*) _buffer;
|
memcpy( _dnsHeader, _buffer, DNS_HEADER_SIZE ) ;
|
||||||
|
if ( requestIncludesOnlyOneQuestion() )
|
||||||
|
{
|
||||||
|
// The QName has a variable length, maximum 255 bytes and is comprised of multiple labels.
|
||||||
|
// Each label contains a byte to describe its length and the label itself. The list of
|
||||||
|
// labels terminates with a zero-valued byte. In "github.com", we have two labels "github" & "com"
|
||||||
|
// Iterate through the labels and copy them as they come into a single buffer (for simplicity's sake)
|
||||||
|
_dnsQuestion->QNameLength = 0 ;
|
||||||
|
while ( _buffer[ DNS_HEADER_SIZE + _dnsQuestion->QNameLength ] != 0 )
|
||||||
|
{
|
||||||
|
memcpy( (void*) &_dnsQuestion->QName[_dnsQuestion->QNameLength], (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ) ;
|
||||||
|
_dnsQuestion->QNameLength += _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ;
|
||||||
|
}
|
||||||
|
_dnsQuestion->QName[_dnsQuestion->QNameLength] = 0 ;
|
||||||
|
_dnsQuestion->QNameLength++ ;
|
||||||
|
|
||||||
|
// Copy the QType and QClass
|
||||||
|
memcpy( &_dnsQuestion->QType, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], sizeof(_dnsQuestion->QType) ) ;
|
||||||
|
memcpy( &_dnsQuestion->QClass, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength + sizeof(_dnsQuestion->QType)], sizeof(_dnsQuestion->QClass) ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_dnsHeader->QR == DNS_QR_QUERY &&
|
if (_dnsHeader->QR == DNS_QR_QUERY &&
|
||||||
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
|
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
|
||||||
@ -87,15 +114,21 @@ bool DNSServer::requestIncludesOnlyOneQuestion()
|
|||||||
_dnsHeader->ARCount == 0;
|
_dnsHeader->ARCount == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String DNSServer::getDomainNameWithoutWwwPrefix()
|
String DNSServer::getDomainNameWithoutWwwPrefix()
|
||||||
{
|
{
|
||||||
|
// Error checking : if the buffer containing the DNS request is a null pointer, return an empty domain
|
||||||
String parsedDomainName = "";
|
String parsedDomainName = "";
|
||||||
if (_buffer == NULL) return parsedDomainName;
|
if (_buffer == NULL)
|
||||||
unsigned char *start = _buffer + 12;
|
return parsedDomainName;
|
||||||
|
|
||||||
|
// Set the start of the domain just after the header (12 bytes). If equal to null character, return an empty domain
|
||||||
|
unsigned char *start = _buffer + DNS_OFFSET_DOMAIN_NAME;
|
||||||
if (*start == 0)
|
if (*start == 0)
|
||||||
{
|
{
|
||||||
return parsedDomainName;
|
return parsedDomainName;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
@ -121,16 +154,35 @@ String DNSServer::getDomainNameWithoutWwwPrefix()
|
|||||||
void DNSServer::replyWithIP()
|
void DNSServer::replyWithIP()
|
||||||
{
|
{
|
||||||
if (_buffer == NULL) return;
|
if (_buffer == NULL) return;
|
||||||
_dnsHeader->QR = DNS_QR_RESPONSE;
|
|
||||||
_dnsHeader->ANCount = _dnsHeader->QDCount;
|
|
||||||
_dnsHeader->QDCount = 0;
|
|
||||||
|
|
||||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
||||||
_udp.write(_buffer, _currentPacketSize);
|
|
||||||
_udp.write((unsigned char*)&_ttl, 4);
|
// Change the type of message to a response and set the number of answers equal to
|
||||||
_udp.write((uint8_t)0);
|
// the number of questions in the header
|
||||||
_udp.write((uint8_t)4);
|
_dnsHeader->QR = DNS_QR_RESPONSE;
|
||||||
_udp.write(_resolvedIP, 4);
|
_dnsHeader->ANCount = _dnsHeader->QDCount;
|
||||||
|
_udp.write( (unsigned char*) _dnsHeader, DNS_HEADER_SIZE ) ;
|
||||||
|
|
||||||
|
// Write the question
|
||||||
|
_udp.write(_dnsQuestion->QName, _dnsQuestion->QNameLength) ;
|
||||||
|
_udp.write( (unsigned char*) &_dnsQuestion->QType, 2 ) ;
|
||||||
|
_udp.write( (unsigned char*) &_dnsQuestion->QClass, 2 ) ;
|
||||||
|
|
||||||
|
// Write the answer
|
||||||
|
// Use DNS name compression : instead of repeating the name in this RNAME occurence,
|
||||||
|
// set the two MSB of the byte corresponding normally to the length to 1. The following
|
||||||
|
// 14 bits must be used to specify the offset of the domain name in the message
|
||||||
|
// (<255 here so the first byte has the 6 LSB at 0)
|
||||||
|
_udp.write((uint8_t) 0xC0);
|
||||||
|
_udp.write((uint8_t) DNS_OFFSET_DOMAIN_NAME);
|
||||||
|
|
||||||
|
// DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
|
||||||
|
uint16_t answerType = htons(DNS_TYPE_A), answerClass = htons(DNS_CLASS_IN), answerIPv4 = htons(DNS_RDLENGTH_IPV4) ;
|
||||||
|
_udp.write((unsigned char*) &answerType, 2 );
|
||||||
|
_udp.write((unsigned char*) &answerClass, 2 );
|
||||||
|
_udp.write((unsigned char*) &_ttl, 4); // DNS Time To Live
|
||||||
|
_udp.write((unsigned char*) &answerIPv4, 2 );
|
||||||
|
_udp.write(_resolvedIP, sizeof(_resolvedIP)); // The IP address to return
|
||||||
_udp.endPacket();
|
_udp.endPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#define DNS_QR_QUERY 0
|
#define DNS_QR_QUERY 0
|
||||||
#define DNS_QR_RESPONSE 1
|
#define DNS_QR_RESPONSE 1
|
||||||
#define DNS_OPCODE_QUERY 0
|
#define DNS_OPCODE_QUERY 0
|
||||||
|
#define DNS_DEFAULT_TTL 60 // Default Time To Live : time interval in seconds that the resource record should be cached before being discarded
|
||||||
|
#define DNS_OFFSET_DOMAIN_NAME 12 // Offset in bytes to reach the domain name in the DNS message
|
||||||
|
#define DNS_HEADER_SIZE 12
|
||||||
|
|
||||||
enum class DNSReplyCode
|
enum class DNSReplyCode
|
||||||
{
|
{
|
||||||
@ -19,6 +22,26 @@ enum class DNSReplyCode
|
|||||||
NXRRSet = 8
|
NXRRSet = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum DNSType
|
||||||
|
{
|
||||||
|
DNS_TYPE_A = 1, // Host Address
|
||||||
|
DNS_TYPE_AAAA = 28, // IPv6 Address
|
||||||
|
DNS_TYPE_SOA = 6, // Start Of a zone of Authority
|
||||||
|
DNS_TYPE_PTR = 12, // Domain name PoinTeR
|
||||||
|
DNS_TYPE_DNAME = 39 // Delegation Name
|
||||||
|
} ;
|
||||||
|
|
||||||
|
enum DNSClass
|
||||||
|
{
|
||||||
|
DNS_CLASS_IN = 1, // INternet
|
||||||
|
DNS_CLASS_CH = 3 // CHaos
|
||||||
|
} ;
|
||||||
|
|
||||||
|
enum DNSRDLength
|
||||||
|
{
|
||||||
|
DNS_RDLENGTH_IPV4 = 4 // 4 bytes for an IPv4 address
|
||||||
|
} ;
|
||||||
|
|
||||||
struct DNSHeader
|
struct DNSHeader
|
||||||
{
|
{
|
||||||
uint16_t ID; // identification number
|
uint16_t ID; // identification number
|
||||||
@ -41,6 +64,14 @@ struct DNSHeader
|
|||||||
uint16_t ARCount; // number of resource entries
|
uint16_t ARCount; // number of resource entries
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DNSQuestion
|
||||||
|
{
|
||||||
|
uint8_t QName[255] ;
|
||||||
|
int8_t QNameLength ;
|
||||||
|
uint16_t QType ;
|
||||||
|
uint16_t QClass ;
|
||||||
|
} ;
|
||||||
|
|
||||||
class DNSServer
|
class DNSServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -66,6 +97,8 @@ class DNSServer
|
|||||||
DNSHeader* _dnsHeader;
|
DNSHeader* _dnsHeader;
|
||||||
uint32_t _ttl;
|
uint32_t _ttl;
|
||||||
DNSReplyCode _errorReplyCode;
|
DNSReplyCode _errorReplyCode;
|
||||||
|
DNSQuestion* _dnsQuestion ;
|
||||||
|
|
||||||
|
|
||||||
void downcaseAndRemoveWwwPrefix(String &domainName);
|
void downcaseAndRemoveWwwPrefix(String &domainName);
|
||||||
String getDomainNameWithoutWwwPrefix();
|
String getDomainNameWithoutWwwPrefix();
|
||||||
|
Loading…
Reference in New Issue
Block a user