Files
GrowController/Firmware/DNSServer.cpp
2021-10-04 13:45:39 +00:00

144 lines
3.6 KiB
C++

#include "./DNSServer.h"
#include <lwip/def.h>
#include <Arduino.h>
#define DEBUG
#define DEBUG_OUTPUT Serial
DNSServer::DNSServer() {
_ttl = htonl(60);
_errorReplyCode = DNSReplyCode::NonExistentDomain;
}
bool DNSServer::start(const uint16_t &port, const String &domainName,
const IPAddress &resolvedIP) {
_port = port;
_domainName = domainName;
_resolvedIP[0] = resolvedIP[0];
_resolvedIP[1] = resolvedIP[1];
_resolvedIP[2] = resolvedIP[2];
_resolvedIP[3] = resolvedIP[3];
downcaseAndRemoveWwwPrefix(_domainName);
return _udp.begin(_port) == 1;
}
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode) {
_errorReplyCode = replyCode;
}
void DNSServer::setTTL(const uint32_t &ttl) {
_ttl = htonl(ttl);
}
void DNSServer::stop() {
_udp.stop();
}
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) {
domainName.toLowerCase();
domainName.replace("www.", "");
}
void DNSServer::processNextRequest() {
_currentPacketSize = _udp.parsePacket();
if (_currentPacketSize) {
_buffer = (unsigned char*) malloc(_currentPacketSize * sizeof(char));
_udp.read(_buffer, _currentPacketSize);
_dnsHeader = (DNSHeader*) _buffer;
if (_dnsHeader->QR == DNS_QR_QUERY &&
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
requestIncludesOnlyOneQuestion() &&
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName)
) {
replyWithIP();
} else if (_dnsHeader->QR == DNS_QR_QUERY) {
replyWithCustomCode();
}
free(_buffer);
}
}
bool DNSServer::requestIncludesOnlyOneQuestion() {
return ntohs(_dnsHeader->QDCount) == 1 &&
_dnsHeader->ANCount == 0 &&
_dnsHeader->NSCount == 0 &&
_dnsHeader->ARCount == 0;
}
String DNSServer::getDomainNameWithoutWwwPrefix() {
String parsedDomainName = "";
unsigned char *start = _buffer + 12;
if (*start == 0) {
return parsedDomainName;
}
int pos = 0;
while (true) {
unsigned char labelLength = *(start + pos);
for (int i = 0; i < labelLength; i++) {
pos++;
parsedDomainName += (char)*(start + pos);
}
pos++;
if (*(start + pos) == 0) {
downcaseAndRemoveWwwPrefix(parsedDomainName);
return parsedDomainName;
} else {
parsedDomainName += ".";
}
}
}
void DNSServer::replyWithIP() {
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->ANCount = _dnsHeader->QDCount;
_dnsHeader->QDCount = _dnsHeader->QDCount;
//_dnsHeader->RA = 1;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, _currentPacketSize);
_udp.write((uint8_t) 192); // answer name is a pointer
_udp.write((uint8_t) 12); // pointer to offset at 0x00c
_udp.write((uint8_t) 0); // 0x0001 answer is type A query (host address)
_udp.write((uint8_t) 1);
_udp.write((uint8_t) 0); //0x0001 answer is class IN (internet address)
_udp.write((uint8_t) 1);
_udp.write((unsigned char*) &_ttl, 4);
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
_udp.write((uint8_t) 0);
_udp.write((uint8_t) 4);
_udp.write(_resolvedIP, sizeof(_resolvedIP));
_udp.endPacket();
#ifdef DEBUG
DEBUG_OUTPUT.print("DNS responds: ");
DEBUG_OUTPUT.print(_resolvedIP[0]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[1]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[2]);
DEBUG_OUTPUT.print(".");
DEBUG_OUTPUT.print(_resolvedIP[3]);
DEBUG_OUTPUT.print(" for ");
DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix());
#endif
}
void DNSServer::replyWithCustomCode() {
_dnsHeader->QR = DNS_QR_RESPONSE;
_dnsHeader->RCode = (unsigned char) _errorReplyCode;
_dnsHeader->QDCount = 0;
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
_udp.write(_buffer, sizeof(DNSHeader));
_udp.endPacket();
}