Created
July 26, 2017 19:46
-
-
Save Cyclenerd/7c9cba13360ec1ec9d2ea36e50c7ff77 to your computer and use it in GitHub Desktop.
Revisions
-
Cyclenerd renamed this gist
Jul 26, 2017 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
Cyclenerd created this gist
Jul 26, 2017 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,165 @@ #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 hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,72 @@ #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 file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,52 @@ /* 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(); }