diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 12cd3e502d8154f497d5e1e55f713d68644670b1..eafa97cfdf95277f5aae77377344d74c3feccd9f 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -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; } @@ -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 && @@ -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) { @@ -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(); } diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index 570e9df6135755d2e11ba40e275aedfa9a3819c9..a10ef9eaaf126dba4ec544ac7e12f0061edd59bf 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -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 @@ -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: @@ -66,6 +97,8 @@ class DNSServer DNSHeader* _dnsHeader; uint32_t _ttl; DNSReplyCode _errorReplyCode; + DNSQuestion* _dnsQuestion ; + void downcaseAndRemoveWwwPrefix(String &domainName); String getDomainNameWithoutWwwPrefix();