Skip to content

Commit

Permalink
DNSServer refactoring, switch to AsyncUDP (#7482)
Browse files Browse the repository at this point in the history
* DNSServer: switch to AsyncUDP instead of WiFiUDP

AsyncUDP offers event driven approch for handling udp dns req's
WiFiUDP hooks to loop() for packet processing and making useless malloc's each run

* DNSServer code refactoring

get rid of intermediate mem buffers and extra data copies,
most of the data could be referenced or copied from the source packet
 - removed _buffer member
 - replaced DNSQuestion.QName from uint8_t[] to char*

added sanity checks for mem bounds
optimize label/packet length calculations
other code cleanup

* DNSServer drop dynamically allocated member structs

DNSHeader and DNSQuestion structs could be created on stack
no need to keep it as obj members

* DNSServer: labels min length checks, simplified labels parser

* DNSServer use default settings for catch-all setup

 - default constructor and start() method simply runs a catch-all DNS setup
 - avoid string comparison for domain reqs in catch-all mode
 - use IPaddress class for _resolvedIP (looking for IPv6 support in future)

* CaptivePortal example refactored

 - use webserver instead of simple tcp setver
 - use redirects to allows CaptivePortal detection pop-ups in modern systems

* DNSServer status getters added

add isUp() method - returns 'true' if server is up and UDP socket is listening for UDP req's
add isCaptive() method - returns 'true' if server runs in catch-all (captive portal mode)
some doxygen comments added
start() method now keeps existing IP address if any

---------

Co-authored-by: Lucas Saavedra Vaz <[email protected]>
Co-authored-by: Me No Dev <[email protected]>
  • Loading branch information
3 people authored Dec 18, 2023
1 parent 44f83b0 commit d912710
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 199 deletions.
81 changes: 44 additions & 37 deletions libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino
Original file line number Diff line number Diff line change
@@ -1,52 +1,59 @@
/*
This example enables catch-all Captive portal for ESP32 Access-Point
It will allow modern devices/OSes to detect that WiFi connection is
limited and offer a user to access a banner web-page.
There is no need to find and open device's IP address/URL, i.e. http://192.168.4.1/
This works for Android, Ubuntu, FireFox, Windows, maybe others...
*/

#include <Arduino.h>
#include <WiFi.h>
#include <DNSServer.h>
#include <WebServer.h>


const byte DNS_PORT = 53;
IPAddress apIP(8,8,4,4); // The default android DNS
DNSServer dnsServer;
WiFiServer server(80);
WebServer server(80);

static const char responsePortal[] = R"===(
<!DOCTYPE html><html><head><title>ESP32 CaptivePortal</title></head><body>
<h1>Hello World!</h1><p>This is a captive portal example page. All unknown http requests will
be redirected here.</p></body></html>
)===";

// index page handler
void handleRoot() {
server.send(200, "text/plain", "Hello from esp32!");
}

String responseHTML = ""
"<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
"<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
"be redirected here.</p></body></html>";
// this will redirect unknown http req's to our captive portal page
// based on this redirect various systems could detect that WiFi AP has a captive portal page
void handleNotFound() {
server.sendHeader("Location", "/portal");
server.send(302, "text/plain", "redirect to captive portal");
}

void setup() {
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAP("ESP32-DNSServer");
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));

// if DNSServer is started with "*" for domain name, it will reply with
// provided IP to all DNS request
dnsServer.start(DNS_PORT, "*", apIP);
// by default DNSServer is started serving any "*" domain name. It will reply
// AccessPoint's IP to all DNS request (this is requred for Captive Portal detection)
dnsServer.start();

// serve a simple root page
server.on("/", handleRoot);

// serve portal page
server.on("/portal",[](){server.send(200, "text/html", responsePortal);});

// all unknown pages are redirected to captive portal
server.onNotFound(handleNotFound);
server.begin();
}

void loop() {
dnsServer.processNextRequest();
WiFiClient client = server.available(); // listen for incoming clients

if (client) {
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n') {
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print(responseHTML);
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
}
}
client.stop();
}
server.handleClient();
delay(5); // give CPU some idle time
}
Loading

0 comments on commit d912710

Please sign in to comment.