From ba70b09d2f5a97f2d93c9e0a24b0aeea71da7a95 Mon Sep 17 00:00:00 2001 From: aalku Date: Thu, 13 Aug 2015 10:06:18 +0200 Subject: [PATCH 1/4] Fixes by SwiCago on DNSServer Fixes by SwiCago on this forum comment http://www.esp8266.com/viewtopic.php?p=23900#p23900 --- .../CaptivePortal2/CaptivePortal2.ino | 100 ++++++++++++++++++ libraries/DNSServer/src/DNSServer.cpp | 34 +++++- libraries/DNSServer/src/DNSServer.h | 2 +- 3 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino diff --git a/libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino b/libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino new file mode 100644 index 000000000..80bf77b1b --- /dev/null +++ b/libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +const char* ssid = "esp8266"; +boolean LEDstate[] = {LOW, false, LOW}; + +const char* html = "Success" + "" + "" + ""; + +const byte DNS_PORT = 53; +IPAddress apIP(192, 168, 1, 1); +IPAddress netMsk(255, 255, 255, 0); +DNSServer dnsServer; +ESP8266WebServer server(80); + +void setup() { + pinMode(0, OUTPUT); + pinMode(2, OUTPUT); + digitalWrite(2, LEDstate[0]); + digitalWrite(2, LEDstate[2]); + Serial.begin(115200); + Serial.setDebugOutput(true); + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(apIP, apIP, netMsk); + WiFi.softAP(ssid); + Serial.print("SSID: "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.softAPIP()); + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(DNS_PORT, "*", apIP); + Serial.println("USP Server started"); + server.on("/", handle_root); + server.on("/generate_204", handle_root); //Android captive portal + server.on("/L0", handle_L0); + server.on("/L2", handle_L2); + server.on("/ALL", handle_ALL); + server.onNotFound(handleNotFound); + server.begin(); + Serial.println("HTTP server started"); + +} + +void loop() { + dnsServer.processNextRequest(); + server.handleClient(); +} + +void handleNotFound() { + Serial.print("\t\t\t\t URI Not Found: "); + Serial.println(server.uri()); + server.send ( 200, "text/plain", "URI Not Found" ); +} + +void handle_root() { + Serial.println("Page served"); + String toSend = html; + toSend.replace("TGT0", LEDstate[0] ? "y" : "b"); + toSend.replace("TGT2", LEDstate[2] ? "y" : "b"); + server.send(200, "text/html", toSend); + delay(100); +} + +void handle_L0() { + change_states(0); + handle_root(); +} + +void handle_L2() { + change_states(2); + handle_root(); +} + +void handle_ALL() { + change_states(0); + change_states(2); + handle_root(); +} + +void change_states(int tgt) { + if (server.hasArg("v")) { + int state = server.arg("v").toInt() == 1; + Serial.print("LED"); + Serial.print(tgt); + Serial.print("="); + Serial.println(state); + LEDstate[tgt] = state ? HIGH : LOW; + digitalWrite(tgt, LEDstate[tgt]); + } +} diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 10fc81d4a..65d2b31d6 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -2,6 +2,9 @@ #include #include +#define DEBUG +#define DEBUG_OUTPUT Serial + DNSServer::DNSServer() { _ttl = htonl(60); @@ -110,16 +113,43 @@ void DNSServer::replyWithIP() { _dnsHeader->QR = DNS_QR_RESPONSE; _dnsHeader->ANCount = _dnsHeader->QDCount; - _dnsHeader->QDCount = 0; + _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() @@ -131,4 +161,4 @@ void DNSServer::replyWithCustomCode() _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); _udp.write(_buffer, sizeof(DNSHeader)); _udp.endPacket(); -} +} \ No newline at end of file diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index d58efbbdd..ca96afea3 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -68,4 +68,4 @@ class DNSServer void replyWithIP(); void replyWithCustomCode(); }; -#endif +#endif \ No newline at end of file From 5cae88827822477c1c76a6e88ce9123bcd9308c4 Mon Sep 17 00:00:00 2001 From: aalku Date: Fri, 14 Aug 2015 22:27:21 +0200 Subject: [PATCH 2/4] LocalIP/LocalPort support --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 16 ++++++++++++++++ libraries/ESP8266WiFi/src/WiFiClient.h | 2 ++ .../ESP8266WiFi/src/include/ClientContext.h | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 50e8d1123..9455ac60e 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -263,6 +263,22 @@ uint16_t WiFiClient::remotePort() return _client->getRemotePort(); } +IPAddress WiFiClient::localIP() +{ + if (!_client) + return IPAddress(0U); + + return IPAddress(_client->getLocalAddress()); +} + +uint16_t WiFiClient::localPort() +{ + if (!_client) + return 0; + + return _client->getLocalPort(); +} + int8_t WiFiClient::_s_connected(void* arg, void* tpcb, int8_t err) { return reinterpret_cast(arg)->_connected(tpcb, err); diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 161ca0be1..fc18a3133 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -60,6 +60,8 @@ public: IPAddress remoteIP(); uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); bool getNoDelay(); void setNoDelay(bool nodelay); static void setLocalPortStart(uint16_t port) { _localPort = port; } diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 619356862..aedd30327 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -125,6 +125,18 @@ class ClientContext { return _pcb->remote_port; } + uint32_t getLocalAddress() { + if(!_pcb) return 0; + + return _pcb->local_ip.addr; + } + + uint16_t getLocalPort() { + if(!_pcb) return 0; + + return _pcb->local_port; + } + size_t getSize() const { if(!_rx_buf) return 0; From 559670baaa7a4c4ab17777130ee1e1d5c795e26a Mon Sep 17 00:00:00 2001 From: aalku Date: Fri, 14 Aug 2015 22:30:15 +0200 Subject: [PATCH 3/4] Host header support --- .../ESP8266WebServer/src/ESP8266WebServer.cpp | 4 +++ .../ESP8266WebServer/src/ESP8266WebServer.h | 4 +++ libraries/ESP8266WebServer/src/Parsing.cpp | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 2f5b68a14..3af856184 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -253,6 +253,10 @@ bool ESP8266WebServer::hasArg(const char* name) { return false; } +String ESP8266WebServer::hostHeader() { + return _hostHeader; +} + void ESP8266WebServer::onFileUpload(THandlerFunction fn) { _fileUploadHandler = fn; } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index ae38eb8cc..3382de3d0 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -80,6 +80,8 @@ public: int args(); // get arguments count bool hasArg(const char* name); // check if argument exists + String hostHeader(); // get request host header if available or empty String if not + // send response to the client // code - HTTP response code, can be 200 or 404 // content_type - HTTP content type, like "text/plain" or "image/png" @@ -134,6 +136,8 @@ protected: size_t _contentLength; String _responseHeaders; + String _hostHeader; + RequestHandler* _firstHandler; RequestHandler* _lastHandler; THandlerFunction _notFoundHandler; diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 682839d13..431c7ee66 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -94,6 +94,14 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { } headerName = req.substring(0, headerDiv); headerValue = req.substring(headerDiv + 2); + + #ifdef DEBUG + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + if (headerName == "Content-Type"){ if (headerValue.startsWith("text/plain")){ isForm = false; @@ -103,6 +111,8 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { } } else if (headerName == "Content-Length"){ contentLength = headerValue.toInt(); + } else if (headerName == "Host"){ + _hostHeader = headerValue; } } @@ -134,6 +144,31 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { _parseForm(client, boundaryStr, contentLength); } } else { + String headerName; + String headerValue; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 2); + + #ifdef DEBUG + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName == "Host"){ + _hostHeader = headerValue; + } + } _parseArguments(searchStr); } client.flush(); From ab8a6d8d45c48d9f2631a7d9cded2f7c9bc8ef85 Mon Sep 17 00:00:00 2001 From: aalku Date: Sun, 16 Aug 2015 20:19:19 +0200 Subject: [PATCH 4/4] Better captive portal example. This example serves a "hello world" on a WLAN and a SoftAP at the same time. The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM. This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/, served by the ESP8266 itself --- .../CaptivePortal2/CaptivePortal2.ino | 100 ------------- .../CaptivePortalAdvanced.ino | 135 ++++++++++++++++++ .../CaptivePortalAdvanced/credentials.ino | 27 ++++ .../CaptivePortalAdvanced/handleHttp.ino | 129 +++++++++++++++++ .../examples/CaptivePortalAdvanced/tools.ino | 21 +++ 5 files changed, 312 insertions(+), 100 deletions(-) delete mode 100644 libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino create mode 100644 libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino create mode 100644 libraries/DNSServer/examples/CaptivePortalAdvanced/credentials.ino create mode 100644 libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino create mode 100644 libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino diff --git a/libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino b/libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino deleted file mode 100644 index 80bf77b1b..000000000 --- a/libraries/DNSServer/examples/CaptivePortal2/CaptivePortal2.ino +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include -#include - -const char* ssid = "esp8266"; -boolean LEDstate[] = {LOW, false, LOW}; - -const char* html = "Success" - "
ONOFF
" + "ONOFF
 
ALL ON
" + "
ALL OFF
" - "" - ""; - -const byte DNS_PORT = 53; -IPAddress apIP(192, 168, 1, 1); -IPAddress netMsk(255, 255, 255, 0); -DNSServer dnsServer; -ESP8266WebServer server(80); - -void setup() { - pinMode(0, OUTPUT); - pinMode(2, OUTPUT); - digitalWrite(2, LEDstate[0]); - digitalWrite(2, LEDstate[2]); - Serial.begin(115200); - Serial.setDebugOutput(true); - WiFi.mode(WIFI_AP); - WiFi.softAPConfig(apIP, apIP, netMsk); - WiFi.softAP(ssid); - Serial.print("SSID: "); - Serial.println(ssid); - Serial.print("IP address: "); - Serial.println(WiFi.softAPIP()); - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(DNS_PORT, "*", apIP); - Serial.println("USP Server started"); - server.on("/", handle_root); - server.on("/generate_204", handle_root); //Android captive portal - server.on("/L0", handle_L0); - server.on("/L2", handle_L2); - server.on("/ALL", handle_ALL); - server.onNotFound(handleNotFound); - server.begin(); - Serial.println("HTTP server started"); - -} - -void loop() { - dnsServer.processNextRequest(); - server.handleClient(); -} - -void handleNotFound() { - Serial.print("\t\t\t\t URI Not Found: "); - Serial.println(server.uri()); - server.send ( 200, "text/plain", "URI Not Found" ); -} - -void handle_root() { - Serial.println("Page served"); - String toSend = html; - toSend.replace("TGT0", LEDstate[0] ? "y" : "b"); - toSend.replace("TGT2", LEDstate[2] ? "y" : "b"); - server.send(200, "text/html", toSend); - delay(100); -} - -void handle_L0() { - change_states(0); - handle_root(); -} - -void handle_L2() { - change_states(2); - handle_root(); -} - -void handle_ALL() { - change_states(0); - change_states(2); - handle_root(); -} - -void change_states(int tgt) { - if (server.hasArg("v")) { - int state = server.arg("v").toInt() == 1; - Serial.print("LED"); - Serial.print(tgt); - Serial.print("="); - Serial.println(state); - LEDstate[tgt] = state ? HIGH : LOW; - digitalWrite(tgt, LEDstate[tgt]); - } -} diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino new file mode 100644 index 000000000..229662651 --- /dev/null +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include + +/* + * This example serves a "hello world" on a WLAN and a SoftAP at the same time. + * The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM. + * + * Connect your computer or cell phone to wifi network ESP_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there. + * Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from ESP_ap and return to your regular WLAN. + * + * Now the ESP8266 is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://esp8266.local too. + * + * This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/ + */ + +/* Set these to your desired softAP credentials. They are not configurable at runtime */ +const char *softAP_ssid = "ESP_ap"; +const char *softAP_password = "12345678"; + +/* hostname for mDNS. Should work at least on windows. Try http://esp8266.local */ +const char *myHostname = "esp8266"; + +/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */ +char ssid[32] = ""; +char password[32] = ""; + +// DNS server +const byte DNS_PORT = 53; +DNSServer dnsServer; + +// Web server +ESP8266WebServer server(80); + +/* Soft AP network parameters */ +IPAddress apIP(192, 168, 4, 1); +IPAddress netMsk(255, 255, 255, 0); + + +/** Should I connect to WLAN asap? */ +boolean connect; + +/** Last time I tried to connect to WLAN */ +long lastConnectTry = 0; + +/** Current WLAN status */ +int status = WL_IDLE_STATUS; + +void setup() { + delay(1000); + Serial.begin(9600); + Serial.println(); + Serial.print("Configuring access point..."); + /* You can remove the password parameter if you want the AP to be open. */ + WiFi.softAPConfig(apIP, apIP, netMsk); + WiFi.softAP(softAP_ssid, softAP_password); + delay(500); // Without delay I've seen the IP address blank + Serial.print("AP IP address: "); + Serial.println(WiFi.softAPIP()); + + /* Setup the DNS server redirecting all the domains to the apIP */ + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(DNS_PORT, "*", apIP); + + /* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */ + server.on("/", handleRoot); + server.on("/wifi", handleWifi); + server.on("/wifisave", handleWifiSave); + server.on("/generate_204", handleRoot); //Android captive portal. Maybe not needed. Might be handled by notFound handler. + server.on("/fwlink", handleRoot); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. + server.onNotFound ( handleNotFound ); + server.begin(); // Web server start + Serial.println("HTTP server started"); + loadCredentials(); // Load WLAN credentials from network + connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID +} + +void connectWifi() { + Serial.println("Connecting as wifi client..."); + WiFi.disconnect(); + WiFi.begin ( ssid, password ); + int connRes = WiFi.waitForConnectResult(); + Serial.print ( "connRes: " ); + Serial.println ( connRes ); +} + +void loop() { + if (connect) { + Serial.println ( "Connect requested" ); + connect = false; + connectWifi(); + lastConnectTry = millis(); + } + { + int s = WiFi.status(); + if (s == 0 && millis() > (lastConnectTry + 60000) ) { + /* If WLAN disconnected and idle try to connect */ + /* Don't set retry time too low as retry interfere the softAP operation */ + connect = true; + } + if (status != s) { // WLAN status change + Serial.print ( "Status: " ); + Serial.println ( s ); + status = s; + if (s == WL_CONNECTED) { + /* Just connected to WLAN */ + Serial.println ( "" ); + Serial.print ( "Connected to " ); + Serial.println ( ssid ); + Serial.print ( "IP address: " ); + Serial.println ( WiFi.localIP() ); + + // Setup MDNS responder + if (!MDNS.begin(myHostname)) { + Serial.println("Error setting up MDNS responder!"); + } else { + Serial.println("mDNS responder started"); + // Add service to MDNS-SD + MDNS.addService("http", "tcp", 80); + } + } else if (s == WL_NO_SSID_AVAIL) { + WiFi.disconnect(); + } + } + } + // Do work: + //DNS + dnsServer.processNextRequest(); + //HTTP + server.handleClient(); +} + diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/credentials.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/credentials.ino new file mode 100644 index 000000000..3f9501e79 --- /dev/null +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/credentials.ino @@ -0,0 +1,27 @@ +/** Load WLAN credentials from EEPROM */ +void loadCredentials() { + EEPROM.begin(512); + EEPROM.get(0, ssid); + EEPROM.get(0+sizeof(ssid), password); + char ok[2+1]; + EEPROM.get(0+sizeof(ssid)+sizeof(password), ok); + EEPROM.end(); + if (String(ok) != String("OK")) { + ssid[0] = 0; + password[0] = 0; + } + Serial.println("Recovered credentials:"); + Serial.println(ssid); + Serial.println(strlen(password)>0?"********":""); +} + +/** Store WLAN credentials to EEPROM */ +void saveCredentials() { + EEPROM.begin(512); + EEPROM.put(0, ssid); + EEPROM.put(0+sizeof(ssid), password); + char ok[2+1] = "OK"; + EEPROM.put(0+sizeof(ssid)+sizeof(password), ok); + EEPROM.commit(); + EEPROM.end(); +} diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino new file mode 100644 index 000000000..4164506ce --- /dev/null +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino @@ -0,0 +1,129 @@ +/** Handle root or redirect to captive portal */ +void handleRoot() { + if (captivePortal()) { // If caprive portal redirect instead of displaying the page. + return; + } + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.sendContent( + "" + "

HELLO WORLD!!

" + ); + if (server.client().localIP() == apIP) { + server.sendContent(String("

You are connected through the soft AP: ") + softAP_ssid + "

"); + } else { + server.sendContent(String("

You are connected through the wifi network: ") + ssid + "

"); + } + server.sendContent( + "

You may want to config the wifi connection.

" + "" + ); + server.client().stop(); // Stop is needed because we sent no content length +} + +/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ +boolean captivePortal() { + if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname)+".local")) { + Serial.print("Request redirected to captive portal"); + server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true); + server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.client().stop(); // Stop is needed because we sent no content length + return true; + } + return false; +} + +/** Wifi config page handler */ +void handleWifi() { + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.sendContent( + "" + "

Wifi config

" + ); + if (server.client().localIP() == apIP) { + server.sendContent(String("

You are connected through the soft AP: ") + softAP_ssid + "

"); + } else { + server.sendContent(String("

You are connected through the wifi network: ") + ssid + "

"); + } + server.sendContent( + "\r\n
" + "
ONOFF
" - "ONOFF
 
ALL ON
" - "
ALL OFF
" + ); + server.sendContent(String() + ""); + server.sendContent(String() + ""); + server.sendContent( + "
SoftAP config
SSID " + String(softAP_ssid) + "
IP " + toStringIp(WiFi.softAPIP()) + "
" + "\r\n
" + "" + ); + server.sendContent(String() + ""); + server.sendContent(String() + ""); + server.sendContent( + "
WLAN config
SSID " + String(ssid) + "
IP " + toStringIp(WiFi.localIP()) + "
" + "\r\n
" + "" + ); + Serial.println("scan start"); + int n = WiFi.scanNetworks(); + Serial.println("scan done"); + if (n > 0) { + for (int i = 0; i < n; i++) { + server.sendContent(String() + "\r\n"); + } + } else { + server.sendContent(String() + ""); + } + server.sendContent( + "
WLAN list (refresh if any missing)
SSID " + String(WiFi.SSID(i)) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")
No WLAN found
" + "\r\n

Connect to network:

" + "" + "
" + "
" + "

You may want to return to the home page.

" + "" + ); + server.client().stop(); // Stop is needed because we sent no content length +} + +/** Handle the WLAN save form and redirect to WLAN config page again */ +void handleWifiSave() { + Serial.println("wifi save"); + server.arg("n").toCharArray(ssid, sizeof(ssid) - 1); + server.arg("p").toCharArray(password, sizeof(password) - 1); + server.sendHeader("Location", "wifi", true); + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.client().stop(); // Stop is needed because we sent no content length + saveCredentials(); + connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID +} + +void handleNotFound() { + if (captivePortal()) { // If caprive portal redirect instead of displaying the error page. + return; + } + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += ( server.method() == HTTP_GET ) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + + for ( uint8_t i = 0; i < server.args(); i++ ) { + message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n"; + } + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + server.send ( 404, "text/plain", message ); +} + diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino new file mode 100644 index 000000000..5b6d78931 --- /dev/null +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino @@ -0,0 +1,21 @@ +/** Is this an IP? */ +boolean isIp(String str) { + for (int i = 0; i < str.length(); i++) { + int c = str.charAt(i); + if (c != '.' && (c < '0' || c > '9')) { + return false; + } + } + return true; +} + +/** IP to String? */ +String toStringIp(IPAddress ip) { + String res = ""; + for (int i = 0; i < 3; i++) { + res += String((ip >> (8 * i)) & 0xFF) + "."; + } + res += String(((ip >> 8 * 3)) & 0xFF); + return res; +} +