Skip to content

Commit 730cff8

Browse files
committed
init
1 parent e94be04 commit 730cff8

File tree

9 files changed

+1751
-0
lines changed

9 files changed

+1751
-0
lines changed

ESP32S2_TFT_AdBlocker/.feather_esp32s2_tft.test.only

Whitespace-only changes.
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#include "AdBlockerDNSServer.h"
2+
#include <lwip/def.h>
3+
#include <Arduino.h>
4+
5+
6+
//// ESP32_AdBlocker
7+
// call back to check if domain blocked and receive IP address to be used
8+
IPAddress checkBlocklist(const char* domainName);
9+
//// ESP32_AdBlocker
10+
11+
DNSServer::DNSServer()
12+
{
13+
_ttl = htonl(DNS_DEFAULT_TTL);
14+
_errorReplyCode = DNSReplyCode::NonExistentDomain;
15+
_dnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ;
16+
_dnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ;
17+
_buffer = NULL;
18+
_currentPacketSize = 0;
19+
_port = 0;
20+
}
21+
22+
bool DNSServer::start(const uint16_t &port, const String &domainName,
23+
const IPAddress &resolvedIP)
24+
{
25+
_port = port;
26+
_buffer = NULL;
27+
_domainName = domainName;
28+
_resolvedIP[0] = resolvedIP[0];
29+
_resolvedIP[1] = resolvedIP[1];
30+
_resolvedIP[2] = resolvedIP[2];
31+
_resolvedIP[3] = resolvedIP[3];
32+
downcaseAndRemoveWwwPrefix(_domainName);
33+
return _udp.begin(_port) == 1;
34+
}
35+
36+
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode)
37+
{
38+
_errorReplyCode = replyCode;
39+
}
40+
41+
void DNSServer::setTTL(const uint32_t &ttl)
42+
{
43+
_ttl = htonl(ttl);
44+
}
45+
46+
void DNSServer::stop()
47+
{
48+
_udp.stop();
49+
free(_buffer);
50+
_buffer = NULL;
51+
}
52+
53+
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName)
54+
{
55+
domainName.toLowerCase();
56+
domainName.replace("www.", "");
57+
}
58+
59+
void DNSServer::processNextRequest()
60+
{
61+
_currentPacketSize = _udp.parsePacket();
62+
if (_currentPacketSize)
63+
{
64+
// Allocate buffer for the DNS query
65+
if (_buffer != NULL)
66+
free(_buffer);
67+
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char));
68+
if (_buffer == NULL)
69+
return;
70+
71+
// Put the packet received in the buffer and get DNS header (beginning of message)
72+
// and the question
73+
_udp.read(_buffer, _currentPacketSize);
74+
memcpy( _dnsHeader, _buffer, DNS_HEADER_SIZE ) ;
75+
if ( requestIncludesOnlyOneQuestion() )
76+
{
77+
// The QName has a variable length, maximum 255 bytes and is comprised of multiple labels.
78+
// Each label contains a byte to describe its length and the label itself. The list of
79+
// labels terminates with a zero-valued byte. In "github.com", we have two labels "github" & "com"
80+
// Iterate through the labels and copy them as they come into a single buffer (for simplicity's sake)
81+
_dnsQuestion->QNameLength = 0 ;
82+
while ( _buffer[ DNS_HEADER_SIZE + _dnsQuestion->QNameLength ] != 0 )
83+
{
84+
memcpy( (void*) &_dnsQuestion->QName[_dnsQuestion->QNameLength], (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ) ;
85+
_dnsQuestion->QNameLength += _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ;
86+
}
87+
_dnsQuestion->QName[_dnsQuestion->QNameLength] = 0 ;
88+
_dnsQuestion->QNameLength++ ;
89+
90+
// Copy the QType and QClass
91+
memcpy( &_dnsQuestion->QType, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], sizeof(_dnsQuestion->QType) ) ;
92+
memcpy( &_dnsQuestion->QClass, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength + sizeof(_dnsQuestion->QType)], sizeof(_dnsQuestion->QClass) ) ;
93+
}
94+
95+
96+
if (_dnsHeader->QR == DNS_QR_QUERY &&
97+
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
98+
requestIncludesOnlyOneQuestion() &&
99+
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName)
100+
)
101+
{
102+
//// ESP32_AdBlocker
103+
IPAddress IPtoUse = checkBlocklist(getDomainNameWithoutWwwPrefix().c_str());
104+
for (int i=0; i<4; i++) _resolvedIP[i] = IPtoUse[i];
105+
//// ESP32_AdBlocker
106+
107+
replyWithIP();
108+
}
109+
else if (_dnsHeader->QR == DNS_QR_QUERY)
110+
{
111+
replyWithCustomCode();
112+
}
113+
114+
free(_buffer);
115+
_buffer = NULL;
116+
}
117+
}
118+
119+
bool DNSServer::requestIncludesOnlyOneQuestion()
120+
{
121+
return ntohs(_dnsHeader->QDCount) == 1 &&
122+
_dnsHeader->ANCount == 0 &&
123+
_dnsHeader->NSCount == 0 &&
124+
_dnsHeader->ARCount == 0;
125+
}
126+
127+
128+
String DNSServer::getDomainNameWithoutWwwPrefix()
129+
{
130+
// Error checking : if the buffer containing the DNS request is a null pointer, return an empty domain
131+
String parsedDomainName = "";
132+
if (_buffer == NULL)
133+
return parsedDomainName;
134+
135+
// Set the start of the domain just after the header (12 bytes). If equal to null character, return an empty domain
136+
unsigned char *start = _buffer + DNS_OFFSET_DOMAIN_NAME;
137+
if (*start == 0)
138+
{
139+
return parsedDomainName;
140+
}
141+
142+
int pos = 0;
143+
while(true)
144+
{
145+
unsigned char labelLength = *(start + pos);
146+
for(int i = 0; i < labelLength; i++)
147+
{
148+
pos++;
149+
parsedDomainName += (char)*(start + pos);
150+
}
151+
pos++;
152+
if (*(start + pos) == 0)
153+
{
154+
downcaseAndRemoveWwwPrefix(parsedDomainName);
155+
return parsedDomainName;
156+
}
157+
else
158+
{
159+
parsedDomainName += ".";
160+
}
161+
}
162+
}
163+
164+
void DNSServer::replyWithIP()
165+
{
166+
if (_buffer == NULL) return;
167+
168+
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
169+
170+
// Change the type of message to a response and set the number of answers equal to
171+
// the number of questions in the header
172+
_dnsHeader->QR = DNS_QR_RESPONSE;
173+
_dnsHeader->ANCount = _dnsHeader->QDCount;
174+
_udp.write( (unsigned char*) _dnsHeader, DNS_HEADER_SIZE ) ;
175+
176+
// Write the question
177+
_udp.write(_dnsQuestion->QName, _dnsQuestion->QNameLength) ;
178+
_udp.write( (unsigned char*) &_dnsQuestion->QType, 2 ) ;
179+
_udp.write( (unsigned char*) &_dnsQuestion->QClass, 2 ) ;
180+
181+
// Write the answer
182+
// Use DNS name compression : instead of repeating the name in this RNAME occurence,
183+
// set the two MSB of the byte corresponding normally to the length to 1. The following
184+
// 14 bits must be used to specify the offset of the domain name in the message
185+
// (<255 here so the first byte has the 6 LSB at 0)
186+
_udp.write((uint8_t) 0xC0);
187+
_udp.write((uint8_t) DNS_OFFSET_DOMAIN_NAME);
188+
189+
// DNS type A : host address, DNS class IN for INternet, returning an IPv4 address
190+
uint16_t answerType = htons(DNS_TYPE_A), answerClass = htons(DNS_CLASS_IN), answerIPv4 = htons(DNS_RDLENGTH_IPV4) ;
191+
_udp.write((unsigned char*) &answerType, 2 );
192+
_udp.write((unsigned char*) &answerClass, 2 );
193+
_udp.write((unsigned char*) &_ttl, 4); // DNS Time To Live
194+
_udp.write((unsigned char*) &answerIPv4, 2 );
195+
_udp.write(_resolvedIP, sizeof(_resolvedIP)); // The IP address to return
196+
_udp.endPacket();
197+
198+
#ifdef DEBUG_ESP_DNS
199+
DBG_OUTPUT_PORT.printf("DNS responds: %s for %s\n",
200+
IPAddress(_resolvedIP).toString().c_str(), getDomainNameWithoutWwwPrefix().c_str() );
201+
#endif
202+
}
203+
204+
void DNSServer::replyWithCustomCode()
205+
{
206+
if (_buffer == NULL) return;
207+
_dnsHeader->QR = DNS_QR_RESPONSE;
208+
_dnsHeader->RCode = (unsigned char)_errorReplyCode;
209+
_dnsHeader->QDCount = 0;
210+
211+
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
212+
_udp.write(_buffer, sizeof(DNSHeader));
213+
_udp.endPacket();
214+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#ifndef DNSServer_h
2+
#define DNSServer_h
3+
#include <WiFiUdp.h>
4+
5+
#define DNS_QR_QUERY 0
6+
#define DNS_QR_RESPONSE 1
7+
#define DNS_OPCODE_QUERY 0
8+
#define DNS_DEFAULT_TTL 60 // Default Time To Live : time interval in seconds that the resource record should be cached before being discarded
9+
#define DNS_OFFSET_DOMAIN_NAME 12 // Offset in bytes to reach the domain name in the DNS message
10+
#define DNS_HEADER_SIZE 12
11+
12+
enum class DNSReplyCode
13+
{
14+
NoError = 0,
15+
FormError = 1,
16+
ServerFailure = 2,
17+
NonExistentDomain = 3,
18+
NotImplemented = 4,
19+
Refused = 5,
20+
YXDomain = 6,
21+
YXRRSet = 7,
22+
NXRRSet = 8
23+
};
24+
25+
enum DNSType
26+
{
27+
DNS_TYPE_A = 1, // Host Address
28+
DNS_TYPE_AAAA = 28, // IPv6 Address
29+
DNS_TYPE_SOA = 6, // Start Of a zone of Authority
30+
DNS_TYPE_PTR = 12, // Domain name PoinTeR
31+
DNS_TYPE_DNAME = 39 // Delegation Name
32+
} ;
33+
34+
enum DNSClass
35+
{
36+
DNS_CLASS_IN = 1, // INternet
37+
DNS_CLASS_CH = 3 // CHaos
38+
} ;
39+
40+
enum DNSRDLength
41+
{
42+
DNS_RDLENGTH_IPV4 = 4 // 4 bytes for an IPv4 address
43+
} ;
44+
45+
struct DNSHeader
46+
{
47+
uint16_t ID; // identification number
48+
union {
49+
struct {
50+
uint16_t RD : 1; // recursion desired
51+
uint16_t TC : 1; // truncated message
52+
uint16_t AA : 1; // authoritive answer
53+
uint16_t OPCode : 4; // message_type
54+
uint16_t QR : 1; // query/response flag
55+
uint16_t RCode : 4; // response code
56+
uint16_t Z : 3; // its z! reserved
57+
uint16_t RA : 1; // recursion available
58+
};
59+
uint16_t Flags;
60+
};
61+
uint16_t QDCount; // number of question entries
62+
uint16_t ANCount; // number of answer entries
63+
uint16_t NSCount; // number of authority entries
64+
uint16_t ARCount; // number of resource entries
65+
};
66+
67+
struct DNSQuestion
68+
{
69+
uint8_t QName[255] ;
70+
int8_t QNameLength ;
71+
uint16_t QType ;
72+
uint16_t QClass ;
73+
} ;
74+
75+
class DNSServer
76+
{
77+
public:
78+
DNSServer();
79+
void processNextRequest();
80+
void setErrorReplyCode(const DNSReplyCode &replyCode);
81+
void setTTL(const uint32_t &ttl);
82+
83+
// Returns true if successful, false if there are no sockets available
84+
bool start(const uint16_t &port,
85+
const String &domainName,
86+
const IPAddress &resolvedIP);
87+
// stops the DNS server
88+
void stop();
89+
90+
private:
91+
WiFiUDP _udp;
92+
uint16_t _port;
93+
String _domainName;
94+
unsigned char _resolvedIP[4];
95+
int _currentPacketSize;
96+
unsigned char* _buffer;
97+
DNSHeader* _dnsHeader;
98+
uint32_t _ttl;
99+
DNSReplyCode _errorReplyCode;
100+
DNSQuestion* _dnsQuestion ;
101+
102+
103+
void downcaseAndRemoveWwwPrefix(String &domainName);
104+
String getDomainNameWithoutWwwPrefix();
105+
bool requestIncludesOnlyOneQuestion();
106+
void replyWithIP();
107+
void replyWithCustomCode();
108+
};
109+
#endif

0 commit comments

Comments
 (0)