ESP8266 : Create a WiFi access point and provide a DNS and web server on it, catch all traffic
/* 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(); | |
} | |
#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(); | |
} | |
#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 | |
This comment has been minimized.
This comment has been minimized.
What do I need to do to get a good readable url for the webserver? (like "http:\IoT.local") |
This comment has been minimized.
This comment has been minimized.
Take a look at https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266mDNS |
This comment has been minimized.
This comment has been minimized.
Is it possible to provide a DNS in Station Mode? |
This comment has been minimized.
This comment has been minimized.
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
This comment has been minimized.
Great! It's work for me. Thanks a lot!