Created
July 26, 2017 19:46
-
-
Save Cyclenerd/7c9cba13360ec1ec9d2ea36e50c7ff77 to your computer and use it in GitHub Desktop.
ESP8266 : Create a WiFi access point and provide a DNS and web server on it, catch all traffic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Create a WiFi access point and provide a web server on it. */ | |
#include <ESP8266WiFi.h> | |
#include "./DNSServer.h" // Patched lib | |
#include <ESP8266WebServer.h> | |
const byte DNS_PORT = 53; // Capture DNS requests on port 53 | |
IPAddress apIP(10, 10, 10, 1); // Private network for server | |
DNSServer dnsServer; // Create the DNS object | |
ESP8266WebServer webServer(80); // HTTP server | |
String responseHTML = "<!DOCTYPE html>" | |
"<html lang=\"en\">" | |
"<head>" | |
"<meta charset=\"utf-8\">" | |
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" | |
"<title>Internet of Bottles</title>" | |
"</head>" | |
"<body>" | |
"<p>I'm just a stupid bottle with WiFi.</p>" | |
"</body>" | |
"</html>"; | |
void setup() { | |
// turn the LED on (HIGH is the voltage level) | |
pinMode(LED_BUILTIN, OUTPUT); | |
digitalWrite(LED_BUILTIN, HIGH); | |
// configure access point | |
WiFi.mode(WIFI_AP); | |
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); | |
WiFi.softAP("IoT --- Free WiFi"); // WiFi name | |
// if DNSServer is started with "*" for domain name, it will reply with | |
// provided IP to all DNS request | |
dnsServer.start(DNS_PORT, "*", apIP); | |
// replay to all requests with same HTML | |
webServer.onNotFound([]() { | |
webServer.send(200, "text/html", responseHTML); | |
}); | |
webServer.begin(); | |
} | |
void loop() { | |
dnsServer.processNextRequest(); | |
webServer.handleClient(); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef DNSServer_h | |
#define DNSServer_h | |
#include <WiFiUdp.h> | |
#define DNS_QR_QUERY 0 | |
#define DNS_QR_RESPONSE 1 | |
#define DNS_OPCODE_QUERY 0 | |
enum class DNSReplyCode | |
{ | |
NoError = 0, | |
FormError = 1, | |
ServerFailure = 2, | |
NonExistentDomain = 3, | |
NotImplemented = 4, | |
Refused = 5, | |
YXDomain = 6, | |
YXRRSet = 7, | |
NXRRSet = 8 | |
}; | |
struct DNSHeader | |
{ | |
uint16_t ID; // identification number | |
unsigned char RD : 1; // recursion desired | |
unsigned char TC : 1; // truncated message | |
unsigned char AA : 1; // authoritive answer | |
unsigned char OPCode : 4; // message_type | |
unsigned char QR : 1; // query/response flag | |
unsigned char RCode : 4; // response code | |
unsigned char Z : 3; // its z! reserved | |
unsigned char RA : 1; // recursion available | |
uint16_t QDCount; // number of question entries | |
uint16_t ANCount; // number of answer entries | |
uint16_t NSCount; // number of authority entries | |
uint16_t ARCount; // number of resource entries | |
}; | |
class DNSServer | |
{ | |
public: | |
DNSServer(); | |
void processNextRequest(); | |
void setErrorReplyCode(const DNSReplyCode &replyCode); | |
void setTTL(const uint32_t &ttl); | |
// Returns true if successful, false if there are no sockets available | |
bool start(const uint16_t &port, | |
const String &domainName, | |
const IPAddress &resolvedIP); | |
// stops the DNS server | |
void stop(); | |
private: | |
WiFiUDP _udp; | |
uint16_t _port; | |
String _domainName; | |
unsigned char _resolvedIP[4]; | |
int _currentPacketSize; | |
unsigned char* _buffer; | |
DNSHeader* _dnsHeader; | |
uint32_t _ttl; | |
DNSReplyCode _errorReplyCode; | |
void downcaseAndRemoveWwwPrefix(String &domainName); | |
String getDomainNameWithoutWwwPrefix(); | |
bool requestIncludesOnlyOneQuestion(); | |
void replyWithIP(); | |
void replyWithCustomCode(); | |
}; | |
#endif | |
What do I need to do to get a good readable url for the webserver? (like "http:\IoT.local")
What do I need to do to get a good readable url for the webserver? (like "http:\IoT.local")
Take a look at https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266mDNS
Is it possible to provide a DNS in Station Mode?
Is it possible to provide a DNS in Station Mode?
Talvez seja melhor dares uma vista de olhos ao esp8266 RTOS SDK .
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great! It's work for me. Thanks a lot!