Skip to content

DNS Server : bug fix and prettifying #1011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 69 additions & 17 deletions libraries/DNSServer/src/DNSServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

DNSServer::DNSServer()
{
_ttl = htonl(60);
_ttl = htonl(DNS_DEFAULT_TTL);
_errorReplyCode = DNSReplyCode::NonExistentDomain;
_dnsHeader = NULL;
_buffer = NULL;
_dnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ;
_dnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ;
_buffer = NULL;
_currentPacketSize = 0;
_port = 0;
}
Expand Down Expand Up @@ -55,11 +56,37 @@ void DNSServer::processNextRequest()
_currentPacketSize = _udp.parsePacket();
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));
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);
_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 &&
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
Expand Down Expand Up @@ -87,15 +114,21 @@ bool DNSServer::requestIncludesOnlyOneQuestion()
_dnsHeader->ARCount == 0;
}


String DNSServer::getDomainNameWithoutWwwPrefix()
{
// Error checking : if the buffer containing the DNS request is a null pointer, return an empty domain
String parsedDomainName = "";
if (_buffer == NULL) return parsedDomainName;
unsigned char *start = _buffer + 12;
if (_buffer == NULL)
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)
{
return parsedDomainName;
}

int pos = 0;
while(true)
{
Expand All @@ -121,16 +154,35 @@ String DNSServer::getDomainNameWithoutWwwPrefix()
void DNSServer::replyWithIP()
{
if (_buffer == NULL) return;
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->ANCount = _dnsHeader->QDCount;
_dnsHeader->QDCount = 0;


_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, _currentPacketSize);
_udp.write((unsigned char*)&_ttl, 4);
_udp.write((uint8_t)0);
_udp.write((uint8_t)4);
_udp.write(_resolvedIP, 4);

// Change the type of message to a response and set the number of answers equal to
// the number of questions in the header
_dnsHeader->QR = DNS_QR_RESPONSE;
_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();
}

Expand Down
47 changes: 40 additions & 7 deletions libraries/DNSServer/src/DNSServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,43 @@
#define DNS_QR_QUERY 0
#define DNS_QR_RESPONSE 1
#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
{
NoError = 0,
NoError = 0,
FormError = 1,
ServerFailure = 2,
ServerFailure = 2,
NonExistentDomain = 3,
NotImplemented = 4,
Refused = 5,
YXDomain = 6,
YXRRSet = 7,
NXRRSet = 8
NotImplemented = 4,
Refused = 5,
YXDomain = 6,
YXRRSet = 7,
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
{
uint16_t ID; // identification number
Expand All @@ -41,6 +64,14 @@ struct DNSHeader
uint16_t ARCount; // number of resource entries
};

struct DNSQuestion
{
uint8_t QName[255] ;
int8_t QNameLength ;
uint16_t QType ;
uint16_t QClass ;
} ;

class DNSServer
{
public:
Expand All @@ -66,6 +97,8 @@ class DNSServer
DNSHeader* _dnsHeader;
uint32_t _ttl;
DNSReplyCode _errorReplyCode;
DNSQuestion* _dnsQuestion ;


void downcaseAndRemoveWwwPrefix(String &domainName);
String getDomainNameWithoutWwwPrefix();
Expand Down