diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/ESP8266mDNS.cpp new file mode 100644 index 000000000..a4a9f3781 --- /dev/null +++ b/libraries/ESP8266mDNS/ESP8266mDNS.cpp @@ -0,0 +1,210 @@ +/* + +ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) +Version 1.1 +Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) +ESP8266 port (c) 2015 Ivan Grokhotkov (igrokhotkov@gmail.com) + +License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +// Important RFC's for reference: +// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt +// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt + +#include "ESP8266mDNS.h" + + +// #define MDNS_DEBUG + +#define HEADER_SIZE 12 +#define QDCOUNT_OFFSET 4 +#define A_RECORD_SIZE 14 +#define NSEC_RECORD_SIZE 20 +#define TTL_OFFSET 4 +#define IP_OFFSET 10 + + +MDNSResponder::MDNSResponder() + : _expected(NULL) + , _expectedLen(0) + , _response(NULL) + , _responseLen(0) + , _index(0) +{ } + +MDNSResponder::~MDNSResponder() { + if (_expected) { + delete[] _expected; + } + if (_response) { + delete[] _response; + } +} + +bool MDNSResponder::begin(const char* domain, IPAddress addr, uint32_t ttlSeconds) +{ + // Construct DNS request/response fully qualified domain name of form: + // , , 5, "local" + size_t n = strlen(domain); + if (n > 255) { + // Can only handle domains that are 255 chars in length. + return false; + } + _expectedLen = 12 + n; + if (_expected) { + delete[] _expected; + } + _expected = new uint8_t[_expectedLen]; + _expected[0] = (uint8_t)n; + + // Copy in domain characters as lowercase + for (int i = 0; i < n; ++i) { + _expected[1+i] = tolower(domain[i]); + } + + // Values for: + // - 5 (length) + // - "local" + // - 0x00 (end of domain) + // - 0x00 0x01 (A record query) + // - 0x00 0x01 (Class IN) + const uint8_t local[] = { 0x05, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x00, 0x00, 0x01, 0x00, 0x01 }; + memcpy(&_expected[1+n], local, 11); + + // Construct DNS query response + // TODO: Move these to flash or just construct in code. + const uint8_t respHeader[] = { 0x00, 0x00, // ID = 0 + 0x84, 0x00, // Flags = response + authoritative answer + 0x00, 0x00, // Question count = 0 + 0x00, 0x01, // Answer count = 1 + 0x00, 0x00, // Name server records = 0 + 0x00, 0x01 // Additional records = 1 + }; + // Generate positive response for IPV4 address + const uint8_t aRecord[] = { 0x00, 0x01, // Type = 1, A record/IPV4 address + 0x80, 0x01, // Class = Internet, with cache flush bit + 0x00, 0x00, 0x00, 0x00, // TTL in seconds, to be filled in later + 0x00, 0x04, // Length of record + 0x00, 0x00, 0x00, 0x00 // IP address, to be filled in later + }; + // Generate negative response for IPV6 address (CC3000 doesn't support IPV6) + const uint8_t nsecRecord[] = { 0xC0, 0x0C, // Name offset + 0x00, 0x2F, // Type = 47, NSEC (overloaded by MDNS) + 0x80, 0x01, // Class = Internet, with cache flush bit + 0x00, 0x00, 0x00, 0x00, // TTL in seconds, to be filled in later + 0x00, 0x08, // Length of record + 0xC0, 0x0C, // Next domain = offset to FQDN + 0x00, // Block number = 0 + 0x04, // Length of bitmap = 4 bytes + 0x40, 0x00, 0x00, 0x00 // Bitmap value = Only first bit (A record/IPV4) is set + }; + + // Allocate memory for response. + int queryFQDNLen = _expectedLen - 4; + _responseLen = HEADER_SIZE + queryFQDNLen + A_RECORD_SIZE + NSEC_RECORD_SIZE; + if (_response) { + delete[] _response; + } + _response = new uint8_t[_responseLen]; + + // Copy data into response. + memcpy(_response, respHeader, HEADER_SIZE); + memcpy(_response + HEADER_SIZE, _expected, queryFQDNLen); + uint8_t* records = _response + HEADER_SIZE + queryFQDNLen; + memcpy(records, aRecord, A_RECORD_SIZE); + memcpy(records + A_RECORD_SIZE, nsecRecord, NSEC_RECORD_SIZE); + + // Add TTL to records. + uint8_t ttl[4] = { (uint8_t)(ttlSeconds >> 24), (uint8_t)(ttlSeconds >> 16), (uint8_t)(ttlSeconds >> 8), (uint8_t)ttlSeconds }; + memcpy(records + TTL_OFFSET, ttl, 4); + memcpy(records + A_RECORD_SIZE + 2 + TTL_OFFSET, ttl, 4); + + // Add IP address to response + + uint32_t ipAddress = (uint32_t) addr; + + records[IP_OFFSET + 3] = (uint8_t)(ipAddress >> 24); + records[IP_OFFSET + 2] = (uint8_t)(ipAddress >> 16); + records[IP_OFFSET + 1] = (uint8_t)(ipAddress >> 8); + records[IP_OFFSET + 0] = (uint8_t) ipAddress; + + // Open the MDNS socket if it isn't already open. + if (!_mdnsConn) { + if (!_mdnsConn.beginMulticast(IPAddress(224, 0, 0, 251), 5353)) { + return false; + } + } + + return true; +} + +void MDNSResponder::update() { + if (!_mdnsConn.parsePacket()) + return; + + // Read available data. + int n = _mdnsConn.available(); + + _index = 0; + +#ifdef MDNS_DEBUG + Serial.println("{"); +#endif + // Look for domain name in request and respond with canned response if found. + for (int i = 0; i < n; ++i) { + uint8_t ch = tolower(_mdnsConn.read()); + +#ifdef MDNS_DEBUG + String str(ch, 16); + Serial.print("0x"); + Serial.print(str); + Serial.print(", "); +#endif + // Check character matches expected. + if (ch == _expected[_index]) + { + _index++; + // Check if domain name was found and send a response. + if (_index == _expectedLen) { + // Send response to multicast address. +#ifdef MDNS_DEBUG + Serial.print("responding, i="); + Serial.println(i); +#endif + _mdnsConn.write(_response, _responseLen); + _mdnsConn.endPacketMulticast(IPAddress(224, 0, 0, 251), 5353); + _index = 0; + } + } + else if (ch == _expected[0]) { + // Found a character that doesn't match, but does match the start of the domain. + _index = 1; + } + else { + // Found a character that doesn't match the expected character or start of domain. + _index = 0; + } + } +#ifdef MDNS_DEBUG + Serial.println("}"); +#endif +} diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.h b/libraries/ESP8266mDNS/ESP8266mDNS.h new file mode 100644 index 000000000..e80c60554 --- /dev/null +++ b/libraries/ESP8266mDNS/ESP8266mDNS.h @@ -0,0 +1,69 @@ +/* +ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) +Version 1.1 +Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) +ESP8266 port (c) 2015 Ivan Grokhotkov (igrokhotkov@gmail.com) + +This is a simple implementation of multicast DNS query support for an Arduino +running on ESP8266 chip. Only support for resolving address queries is currently +implemented. + +Requirements: +- ESP8266WiFi library + +Usage: +- Include the ESP8266 Multicast DNS library in the sketch. +- Create an instance of the MDNSResponder class. +- Call the begin method in the sketch's setup and provide a domain name (without + the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the + Adafruit CC3000 class instance. Optionally provide a time to live (in seconds) + for the DNS record--the default is 1 hour. +- Call the update method in each iteration of the sketch's loop function. + +License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ +#ifndef ESP8266MDNS_H +#define ESP8266MDNS_H + +#include "ESP8266WiFi.h" +#include "WiFiUdp.h" + +class MDNSResponder { +public: + MDNSResponder(); + ~MDNSResponder(); + bool begin(const char* domain, IPAddress addr, uint32_t ttlSeconds = 3600); + void update(); + +private: + // Expected query values + uint8_t* _expected; + int _expectedLen; + // Current parsing state + int _index; + // Response data + uint8_t* _response; + int _responseLen; + // Socket for MDNS communication + WiFiUDP _mdnsConn; +}; + +#endif //ESP8266MDNS_H diff --git a/libraries/ESP8266mDNS/README.md b/libraries/ESP8266mDNS/README.md new file mode 100644 index 000000000..80b820b1f --- /dev/null +++ b/libraries/ESP8266mDNS/README.md @@ -0,0 +1,52 @@ +ESP8266 Multicast DNS +==================== + +A port of CC3000 Multicast DNS library (version 1.1) + +This is a simple implementation of multicast DNS query support for an Arduino +running on ESP8266 chip. Only support for resolving address queries is currently +implemented. + +Requirements +------------ +- ESP8266WiFi library +- MDNS support in your operating system/client machines: + - For Mac OSX support is built in through Bonjour already. + - For Linux, install [Avahi](http://avahi.org/). + - For Windows, install [Bonjour](http://www.apple.com/support/bonjour/). + +Usage +----- +1. Download this repository as a zip (button on the right) and follow [these instructions to install into Arduino](http://arduino.cc/en/Guide/Libraries). +2. Include the ESP8266mDNS library in the sketch. +3. Create an instance of the MDNSResponder class. +4. Call the begin method in the sketch's setup and provide a domain name (without + the '.local' suffix, i.e. just provide 'foo' to resolve 'foo.local'), and the + IP address to advertise. Optionally provide a time to live (in seconds) + for the DNS record--the default is 1 hour. +5. Call the update method in each iteration of the sketch's loop function. + +See the included MDNS + HTTP server sketch for a full example. + +License +------- +Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) +ESP8266 port (c) 2015 Ivan Grokhotkov (igrokhotkov@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino b/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino new file mode 100644 index 000000000..4cc39ab7a --- /dev/null +++ b/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino @@ -0,0 +1,124 @@ +/* + ESP8266 mDNS responder sample + + This is an example of an HTTP server that is accessible + via http://esp8266.local URL thanks to mDNS responder. + + Instructions: + - Update WiFi SSID and password as necessary. + - Flash the sketch to the ESP8266 board + - Install host software: + - For Linux, install Avahi (http://avahi.org/). + - For Windows, install Bonjour (http://www.apple.com/support/bonjour/). + - For Mac OSX and iOS support is built in through Bonjour already. + - Point your browser to http://esp8266.local, you should see a response. + + */ + + +#include +#include +#include + +const char* ssid = "............"; +const char* password = ".............."; + +// multicast DNS responder +MDNSResponder mdns; + +// TCP server at port 80 will respond to HTTP requests +WiFiServer server(80); + +void setup(void) +{ + Serial.begin(115200); + + // Connect to WiFi network + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + // Set up mDNS responder: + // - first argument is the domain name, in this example + // the fully-qualified domain name is "esp8266.local" + // - second argument is the IP address to advertise + // we send our IP address on the WiFi network + // Note: for AP mode we would use WiFi.softAPIP()! + if (!mdns.begin("esp8266", WiFi.localIP())) { + Serial.println("Error setting up MDNS responder!"); + while(1) { + delay(1000); + } + } + Serial.println("mDNS responder started"); + + // Start TCP (HTTP) server + server.begin(); + Serial.println("TCP server started"); +} + +void loop(void) +{ + // Check for any mDNS queries and send responses + mdns.update(); + + // Check if a client has connected + WiFiClient client = server.available(); + if (!client) { + return; + } + Serial.println(""); + Serial.println("New client"); + + // Wait for data from client to become available + while(client.connected() && !client.available()){ + delay(1); + } + + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); + + // First line of HTTP request looks like "GET /path HTTP/1.1" + // Retrieve the "/path" part by finding the spaces + int addr_start = req.indexOf(' '); + int addr_end = req.indexOf(' ', addr_start + 1); + if (addr_start == -1 || addr_end == -1) { + Serial.print("Invalid request: "); + Serial.println(req); + return; + } + req = req.substring(addr_start + 1, addr_end); + Serial.print("Request: "); + Serial.println(req); + client.flush(); + + String s; + if (req == "/") + { + IPAddress ip = WiFi.localIP(); + String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]); + s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n\r\nHello from ESP8266 at "; + s += ipStr; + s += "\r\n\r\n"; + Serial.println("Sending 200"); + } + else + { + s = "HTTP/1.1 404 Not Found\r\n\r\n"; + Serial.println("Sending 404"); + } + client.print(s); + + Serial.println("Done with client"); +} + diff --git a/libraries/ESP8266mDNS/keywords.txt b/libraries/ESP8266mDNS/keywords.txt new file mode 100644 index 000000000..64dff9102 --- /dev/null +++ b/libraries/ESP8266mDNS/keywords.txt @@ -0,0 +1,21 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +MDNSResponder KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +update KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +