Skip to content

Commit f1bad67

Browse files
authored
Merge pull request #2147 from ladyada/main
esp32-s2 tft 'nah' demo
2 parents cf97c03 + 3839905 commit f1bad67

File tree

10 files changed

+1775
-6
lines changed

10 files changed

+1775
-6
lines changed

ESP32S2_TFT_AdBlocker/.feather_esp32s2_tft.test.only

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

0 commit comments

Comments
 (0)