From 15ceed0aac12aafeb3d7b01cad094b5d85b5df00 Mon Sep 17 00:00:00 2001 From: Laurent Louf Date: Fri, 19 Jan 2018 17:08:19 +0100 Subject: [PATCH 1/5] Retrieve some code from what has been done on the ESP8266. Clarify a bit the signification of several bytes in the response. --- libraries/DNSServer/src/DNSServer.cpp | 23 +++++++++++++++++++---- libraries/DNSServer/src/DNSServer.h | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 12cd3e502d8..e9b2c35288f 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -123,14 +123,29 @@ void DNSServer::replyWithIP() if (_buffer == NULL) return; _dnsHeader->QR = DNS_QR_RESPONSE; _dnsHeader->ANCount = _dnsHeader->QDCount; - _dnsHeader->QDCount = 0; + //_dnsHeader->QDCount = 0; _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); _udp.write(_buffer, _currentPacketSize); + + // 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 + _udp.write((uint8_t) (DNS_TYPE_A >> 8) ); + _udp.write((uint8_t) (DNS_TYPE_A & 0xFF) ); + // DNS class IN for INternet + _udp.write((uint8_t) (DNS_CLASS_IN >> 8) ); + _udp.write((uint8_t) (DNS_CLASS_IN & 0xFF) ); + // DNS Time To Live _udp.write((unsigned char*)&_ttl, 4); - _udp.write((uint8_t)0); - _udp.write((uint8_t)4); - _udp.write(_resolvedIP, 4); + // Returning an IPv4 address + _udp.write((uint8_t) (DNS_RDLENGTH_IPV4 >> 8) ); + _udp.write((uint8_t) (DNS_RDLENGTH_IPV4 & 0xFF) ); + _udp.write(_resolvedIP, sizeof(_resolvedIP)); _udp.endPacket(); } diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index 570e9df6135..edc897c4161 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -5,6 +5,7 @@ #define DNS_QR_QUERY 0 #define DNS_QR_RESPONSE 1 #define DNS_OPCODE_QUERY 0 +#define DNS_OFFSET_DOMAIN_NAME 12 // Offset in bytes to reach the domain name in the DNS message enum class DNSReplyCode { @@ -19,6 +20,26 @@ enum class DNSReplyCode 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 From 7d13c51cc7d1c82c5e917b5872166377189443a8 Mon Sep 17 00:00:00 2001 From: Laurent Louf Date: Fri, 19 Jan 2018 17:16:49 +0100 Subject: [PATCH 2/5] Add the type and class as members of the DNS class for an eventual future use. --- libraries/DNSServer/src/DNSServer.cpp | 2 ++ libraries/DNSServer/src/DNSServer.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index e9b2c35288f..ded0a9392fa 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -60,6 +60,8 @@ void DNSServer::processNextRequest() if (_buffer == NULL) return; _udp.read(_buffer, _currentPacketSize); _dnsHeader = (DNSHeader*) _buffer; + _type = (uint16_t) ( (_buffer[_currentPacketSize - 4] << 8) + _buffer[_currentPacketSize - 3] ) ; + _class = (uint16_t) ( (_buffer[_currentPacketSize - 2] << 8) + _buffer[_currentPacketSize - 1] ) ; if (_dnsHeader->QR == DNS_QR_QUERY && _dnsHeader->OPCode == DNS_OPCODE_QUERY && diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index edc897c4161..b13242ee5c0 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -87,6 +87,8 @@ class DNSServer DNSHeader* _dnsHeader; uint32_t _ttl; DNSReplyCode _errorReplyCode; + uint16_t _type ; + uint16_t _class ; void downcaseAndRemoveWwwPrefix(String &domainName); String getDomainNameWithoutWwwPrefix(); From 3da65404baeced3533fa31308d1692e547c0d709 Mon Sep 17 00:00:00 2001 From: Laurent Louf Date: Fri, 19 Jan 2018 17:19:59 +0100 Subject: [PATCH 3/5] Clarify the sense of a magic number present in DNS server. --- libraries/DNSServer/src/DNSServer.cpp | 2 +- libraries/DNSServer/src/DNSServer.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index ded0a9392fa..c03b0935aa6 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -5,7 +5,7 @@ DNSServer::DNSServer() { - _ttl = htonl(60); + _ttl = htonl(DNS_DEFAULT_TTL); _errorReplyCode = DNSReplyCode::NonExistentDomain; _dnsHeader = NULL; _buffer = NULL; diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index b13242ee5c0..1dbf80bbc90 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -5,6 +5,7 @@ #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 enum class DNSReplyCode From 63541cfb0c486e06dee56f25f32778f90ad8479d Mon Sep 17 00:00:00 2001 From: Laurent Louf Date: Fri, 19 Jan 2018 17:24:33 +0100 Subject: [PATCH 4/5] A bit of aesthetics for the DNS server. --- libraries/DNSServer/src/DNSServer.cpp | 26 +++++++++++++++++++------- libraries/DNSServer/src/DNSServer.h | 14 +++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index c03b0935aa6..8c3baae5800 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -7,8 +7,8 @@ DNSServer::DNSServer() { _ttl = htonl(DNS_DEFAULT_TTL); _errorReplyCode = DNSReplyCode::NonExistentDomain; - _dnsHeader = NULL; - _buffer = NULL; + _dnsHeader = NULL; + _buffer = NULL; _currentPacketSize = 0; _port = 0; } @@ -55,11 +55,17 @@ 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), + // type and class (last 4 bytes of the message) _udp.read(_buffer, _currentPacketSize); - _dnsHeader = (DNSHeader*) _buffer; + _dnsHeader = (DNSHeader*) _buffer; _type = (uint16_t) ( (_buffer[_currentPacketSize - 4] << 8) + _buffer[_currentPacketSize - 3] ) ; _class = (uint16_t) ( (_buffer[_currentPacketSize - 2] << 8) + _buffer[_currentPacketSize - 1] ) ; @@ -89,15 +95,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) { diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index 1dbf80bbc90..b68e60c06c8 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -10,15 +10,15 @@ 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 From 60b1166e27f30d53d244863e224e8f28f550e825 Mon Sep 17 00:00:00 2001 From: Laurent Louf Date: Fri, 2 Feb 2018 17:34:05 +0100 Subject: [PATCH 5/5] Add a structure for the DNS question, use it DNS server to store the question data and to create the DNS answer from scratch. --- libraries/DNSServer/src/DNSServer.cpp | 67 ++++++++++++++++++--------- libraries/DNSServer/src/DNSServer.h | 13 +++++- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 8c3baae5800..eafa97cfdf9 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -7,7 +7,8 @@ DNSServer::DNSServer() { _ttl = htonl(DNS_DEFAULT_TTL); _errorReplyCode = DNSReplyCode::NonExistentDomain; - _dnsHeader = NULL; + _dnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ; + _dnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ; _buffer = NULL; _currentPacketSize = 0; _port = 0; @@ -62,12 +63,30 @@ void DNSServer::processNextRequest() if (_buffer == NULL) return; - // Put the packet received in the buffer and get DNS header (beginning of message), - // type and class (last 4 bytes of the message) + // Put the packet received in the buffer and get DNS header (beginning of message) + // and the question _udp.read(_buffer, _currentPacketSize); - _dnsHeader = (DNSHeader*) _buffer; - _type = (uint16_t) ( (_buffer[_currentPacketSize - 4] << 8) + _buffer[_currentPacketSize - 3] ) ; - _class = (uint16_t) ( (_buffer[_currentPacketSize - 2] << 8) + _buffer[_currentPacketSize - 1] ) ; + 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 && @@ -135,31 +154,35 @@ String DNSServer::getDomainNameWithoutWwwPrefix() void DNSServer::replyWithIP() { if (_buffer == NULL) return; - _dnsHeader->QR = DNS_QR_RESPONSE; + + _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); + + // 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; - //_dnsHeader->QDCount = 0; + _udp.write( (unsigned char*) _dnsHeader, DNS_HEADER_SIZE ) ; - _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); - _udp.write(_buffer, _currentPacketSize); + // 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 - _udp.write((uint8_t) (DNS_TYPE_A >> 8) ); - _udp.write((uint8_t) (DNS_TYPE_A & 0xFF) ); - // DNS class IN for INternet - _udp.write((uint8_t) (DNS_CLASS_IN >> 8) ); - _udp.write((uint8_t) (DNS_CLASS_IN & 0xFF) ); - // DNS Time To Live - _udp.write((unsigned char*)&_ttl, 4); - // Returning an IPv4 address - _udp.write((uint8_t) (DNS_RDLENGTH_IPV4 >> 8) ); - _udp.write((uint8_t) (DNS_RDLENGTH_IPV4 & 0xFF) ); - _udp.write(_resolvedIP, sizeof(_resolvedIP)); + + // 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(); } diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index b68e60c06c8..a10ef9eaaf1 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -7,6 +7,7 @@ #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 { @@ -63,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: @@ -88,8 +97,8 @@ class DNSServer DNSHeader* _dnsHeader; uint32_t _ttl; DNSReplyCode _errorReplyCode; - uint16_t _type ; - uint16_t _class ; + DNSQuestion* _dnsQuestion ; + void downcaseAndRemoveWwwPrefix(String &domainName); String getDomainNameWithoutWwwPrefix();