From b5a65cfdface89a15d6c2a59e964b043332f394f Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Fri, 11 Mar 2016 14:06:23 -0500 Subject: [PATCH 1/5] Add beginMulti API for Multicast UDP support --- keywords.txt | 1 + src/WiFiUdp.cpp | 13 +++++++++++++ src/WiFiUdp.h | 1 + 3 files changed, 15 insertions(+) diff --git a/keywords.txt b/keywords.txt index 6ee1d3e6..422b121c 100755 --- a/keywords.txt +++ b/keywords.txt @@ -25,6 +25,7 @@ flush KEYWORD2 stop KEYWORD2 connected KEYWORD2 begin KEYWORD2 +beginMulti KEYWORD2 disconnect KEYWORD2 macAddress KEYWORD2 localIP KEYWORD2 diff --git a/src/WiFiUdp.cpp b/src/WiFiUdp.cpp index edb7d928..da2e7ecc 100644 --- a/src/WiFiUdp.cpp +++ b/src/WiFiUdp.cpp @@ -92,6 +92,19 @@ uint8_t WiFiUDP::begin(uint16_t port) return 1; } +uint8_t WiFiUDP::beginMulti(IPAddress ip, uint16_t port) +{ + uint32_t multiIp = ip; + + if (!begin(port)) { + return 0; + } + + setsockopt(_socket, SOL_SOCKET, IP_ADD_MEMBERSHIP, &multiIp, sizeof(multiIp)); + + return 1; +} + /* return number of bytes available in the current packet, will return zero if parsePacket hasn't been called yet */ int WiFiUDP::available() diff --git a/src/WiFiUdp.h b/src/WiFiUdp.h index d089fcb6..039b4ac3 100644 --- a/src/WiFiUdp.h +++ b/src/WiFiUdp.h @@ -38,6 +38,7 @@ class WiFiUDP : public UDP { public: WiFiUDP(); // Constructor virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulti(IPAddress, uint16_t); // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 if there are no sockets available to use virtual void stop(); // Finish with the UDP socket // Sending UDP packets From c9fb5eab5948197df7a069513cfcdc49ebceeb61 Mon Sep 17 00:00:00 2001 From: Tony DiCola Date: Wed, 27 Jan 2016 15:35:16 -0800 Subject: [PATCH 2/5] Add MDNS responder class for multicast DNS name query support. See example MDNS_WiFiWebServer for usage. --- .../MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino | 193 +++++++++++++++++ src/Adafruit_WINC1500MDNS.cpp | 197 ++++++++++++++++++ src/Adafruit_WINC1500MDNS.h | 54 +++++ src/WiFiUdp.cpp | 12 +- src/WiFiUdp.h | 4 +- 5 files changed, 452 insertions(+), 8 deletions(-) create mode 100644 examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino create mode 100644 src/Adafruit_WINC1500MDNS.cpp create mode 100644 src/Adafruit_WINC1500MDNS.h diff --git a/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino b/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino new file mode 100644 index 00000000..639377e2 --- /dev/null +++ b/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino @@ -0,0 +1,193 @@ +/* + MDNS WiFi Web Server + + A simple web server that shows the value of the analog input pins, + and exposes itself on the MDNS name 'winc1500.local'. + + On Linux (like Ubuntu 15.04) or OSX you can access the web page + on the device in a browser at 'http://winc1500.local/'. + + On Windows you'll first need to install the Bonjour Printer Services + from: + https://support.apple.com/kb/dl999?locale=en_US + Then you can access the device in a browser at 'http://winc1500.local/'. + + This example is written for a network using WPA encryption. For + WEP or WPA, change the Wifi.begin() call accordingly. + + Circuit: + * WiFi shield attached + * Analog inputs attached to pins A0 through A5 (optional) + + created 13 July 2010 + by dlf (Metodo2 srl) + modified 31 May 2012 + by Tom Igoe + modified 27 January 2016 + by Tony DiCola + +*/ + +#include +#include +#include + +// Define the MDNS name that the board will respond to: +#define MDNS_NAME "winc1500" +// Note that the actual MDNS name will have '.local' after +// the name above, so "winc1500" will be accessible on +// the MDNS name "winc1500.local". + +// Define the WINC1500 board connections below. +// If you're following the Adafruit WINC1500 board +// guide you don't need to modify these: +#define WINC_CS 8 +#define WINC_IRQ 7 +#define WINC_RST 4 +#define WINC_EN 2 // or, tie EN to VCC and comment this out +// The SPI pins of the WINC1500 (SCK, MOSI, MISO) should be +// connected to the hardware SPI port of the Arduino. +// On an Uno or compatible these are SCK = #13, MISO = #12, MOSI = #11. +// On an Arduino Zero use the 6-pin ICSP header, see: +// https://www.arduino.cc/en/Reference/SPI + +// Setup the WINC1500 connection with the pins above and the default hardware SPI. +Adafruit_WINC1500 WiFi(WINC_CS, WINC_IRQ, WINC_RST); + +// Or just use hardware SPI (SCK/MOSI/MISO) and defaults, SS -> #10, INT -> #7, RST -> #5, EN -> 3-5V +//Adafruit_WINC1500 WiFi; + +char ssid[] = "yourNetwork"; // your network SSID (name) +char pass[] = "secretPassword"; // your network password +int keyIndex = 0; // your network key Index number (needed only for WEP) + +int status = WL_IDLE_STATUS; + +// Create a MDNS responder to listen and respond to MDNS name requests. +MDNSResponder mdns(&WiFi); // Need to pass in a reference to the WiFi class above. + +Adafruit_WINC1500Server server(80); + +void setup() { +#ifdef WINC_EN + pinMode(WINC_EN, OUTPUT); + digitalWrite(WINC_EN, HIGH); +#endif + + //Initialize serial and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // check for the presence of the shield: + if (WiFi.status() == WL_NO_SHIELD) { + Serial.println("WiFi shield not present"); + // don't continue: + while (true); + } + + // attempt to connect to Wifi network: + while ( status != WL_CONNECTED) { + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + // wait 10 seconds for connection: + delay(10000); + } + // you're connected now, so print out the status: + printWifiStatus(); + + server.begin(); + + // Setup the MDNS responder to listen to the configured name. + // NOTE: You _must_ call this _after_ connecting to the WiFi network and + // being assigned an IP address. + if (!mdns.begin(MDNS_NAME)) { + Serial.println("Failed to start MDNS responder!"); + while(1); + } + + Serial.print("Server listening at http://"); + Serial.print(MDNS_NAME); + Serial.println(".local/"); +} + + +void loop() { + // Call the update() function on the MDNS responder every loop iteration to + // make sure it can detect and respond to name requests. + mdns.update(); + + // listen for incoming clients + Adafruit_WINC1500Client client = server.available(); + if (client) { + Serial.println("new client"); + // an http request ends with a blank line + boolean currentLineIsBlank = true; + while (client.connected()) { + if (client.available()) { + char c = client.read(); + Serial.write(c); + // if you've gotten to the end of the line (received a newline + // character) and the line is blank, the http request has ended, + // so you can send a reply + if (c == '\n' && currentLineIsBlank) { + // send a standard http response header + client.println("HTTP/1.1 200 OK"); + client.println("Content-Type: text/html"); + client.println("Connection: close"); // the connection will be closed after completion of the response + client.println("Refresh: 5"); // refresh the page automatically every 5 sec + client.println(); + client.println(""); + client.println(""); + // output the value of each analog input pin + for (int analogChannel = 0; analogChannel < 6; analogChannel++) { + int sensorReading = analogRead(analogChannel); + client.print("analog input "); + client.print(analogChannel); + client.print(" is "); + client.print(sensorReading); + client.println("
"); + } + client.println(""); + break; + } + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } + else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + // give the web browser time to receive the data + delay(1); + + // close the connection: + client.stop(); + Serial.println("client disconnected"); + } +} + + +void printWifiStatus() { + // print the SSID of the network you're attached to: + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print your WiFi shield's IP address: + IPAddress ip = WiFi.localIP(); + Serial.print("IP Address: "); + Serial.println(ip); + + // print the received signal strength: + long rssi = WiFi.RSSI(); + Serial.print("signal strength (RSSI):"); + Serial.print(rssi); + Serial.println(" dBm"); +} diff --git a/src/Adafruit_WINC1500MDNS.cpp b/src/Adafruit_WINC1500MDNS.cpp new file mode 100644 index 00000000..cbf4a09d --- /dev/null +++ b/src/Adafruit_WINC1500MDNS.cpp @@ -0,0 +1,197 @@ +// Port of CC3000 MDNS Responder to WINC1500. +// Author: Tony DiCola +// +// This MDNSResponder class implements just enough MDNS functionality to respond +// to name requests, for example 'foo.local'. This does not implement any other +// MDNS or Bonjour functionality like services, etc. +// +// Copyright (c) 2016 Adafruit Industries. All right reserved. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#include "Arduino.h" +#include "Adafruit_WINC1500MDNS.h" + +// 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 + +#define READ_BUFFER_SIZE 20 +#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(Adafruit_WINC1500* wifi) + : _expected(NULL) + , _expectedLen(0) + , _response(NULL) + , _responseLen(0) + , _index(0) + , _mdnsSocket() + , _wifi(wifi) +{ } + +MDNSResponder::~MDNSResponder() { + if (_expected != NULL) { + free(_expected); + } + if (_response != NULL) { + free(_response); + } +} + +bool MDNSResponder::begin(const char* domain, 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 != NULL) { + free(_expected); + } + _expected = (uint8_t*) malloc(_expectedLen); + if (_expected == NULL) { + return false; + } + _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) + 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. + 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 + 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) + 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 != NULL) { + free(_response); + } + _response = (uint8_t*) malloc(_responseLen); + if (_response == NULL) { + return false; + } + // 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 = _wifi->localIP(); + records[IP_OFFSET] = (uint8_t) ipAddress; + records[IP_OFFSET + 1] = (uint8_t)(ipAddress >> 8); + records[IP_OFFSET + 2] = (uint8_t)(ipAddress >> 16); + records[IP_OFFSET + 3] = (uint8_t)(ipAddress >> 24); + + // Open the MDNS UDP listening socket on port 5353 with multicast address + // 224.0.0.251 (0xE00000FB) + if (!_mdnsSocket.begin(5353, 0xE00000FB)) { + return false; + } + + return true; +} + +void MDNSResponder::update() { + // Check if there's data to read from the UDP socket. + int available = _mdnsSocket.parsePacket(); + // Stop processing if no data is available. + if (available <= 0) { + return; + } + // Otherwise there is data to read so grab it all and parse the data. + uint8_t buffer[READ_BUFFER_SIZE]; + int n = _mdnsSocket.read((unsigned char*)&buffer, sizeof(buffer)); + if (n < 1) { + // Error getting data. + return; + } + // Look for domain name in request and respond with canned response if found. + for (int i = 0; i < n; ++i) { + uint8_t ch = tolower(buffer[i]); + // 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. + _broadcastResponse(); + _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; + } + } +} + +void MDNSResponder::_broadcastResponse() { + // Send a MDNS name query response for this device. + // Use the MDNS multicast address 224.0.0.251 port 5353. + _mdnsSocket.beginPacket(IPAddress(224, 0, 0, 251), 5353); + _mdnsSocket.write(_response, _responseLen); + _mdnsSocket.endPacket(); +} diff --git a/src/Adafruit_WINC1500MDNS.h b/src/Adafruit_WINC1500MDNS.h new file mode 100644 index 00000000..f538bf52 --- /dev/null +++ b/src/Adafruit_WINC1500MDNS.h @@ -0,0 +1,54 @@ +// Port of CC3000 MDNS Responder to WINC1500. +// Author: Tony DiCola +// +// This MDNSResponder class implements just enough MDNS functionality to respond +// to name requests, for example 'foo.local'. This does not implement any other +// MDNS or Bonjour functionality like services, etc. +// +// Copyright (c) 2016 Adafruit Industries. All right reserved. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#ifndef ADAFRUIT_WINC1500MDNS_H +#define ADAFRUIT_WINC1500MDNS_H + +#include "Adafruit_WINC1500.h" +#include "Adafruit_WINC1500Udp.h" + +class MDNSResponder { +public: + MDNSResponder(Adafruit_WINC1500* wifi); + ~MDNSResponder(); + bool begin(const char* domain, uint32_t ttlSeconds = 3600); + void update(); + +private: + void _broadcastResponse(); + + // Expected query values + static uint8_t _queryHeader[]; + uint8_t* _expected; + int _expectedLen; + // Current parsing state + int _index; + // Response data + uint8_t* _response; + int _responseLen; + // UDP socket for receiving/sending MDNS data. + Adafruit_WINC1500UDP _mdnsSocket; + // Reference to WINC1500 wifi object, used to get IP address. + Adafruit_WINC1500* _wifi; +}; + +#endif diff --git a/src/WiFiUdp.cpp b/src/WiFiUdp.cpp index da2e7ecc..0d6e8cd8 100644 --- a/src/WiFiUdp.cpp +++ b/src/WiFiUdp.cpp @@ -110,7 +110,7 @@ uint8_t WiFiUDP::beginMulti(IPAddress ip, uint16_t port) int WiFiUDP::available() { m2m_wifi_handle_events(NULL); - + if (_socket != -1) { return _rcvSize; } @@ -122,7 +122,7 @@ void WiFiUDP::stop() { if (_socket < 0) return; - + socketBufferUnregister(_socket); close(_socket); _socket = -1; @@ -143,7 +143,7 @@ int WiFiUDP::beginPacket(IPAddress ip, uint16_t port) { _sndIP = ip; _sndPort = port; - + return 1; } @@ -187,7 +187,7 @@ size_t WiFiUDP::write(const uint8_t *buffer, size_t size) int WiFiUDP::parsePacket() { m2m_wifi_handle_events(NULL); - + if (_socket != -1) { if (_rcvSize != 0) { return _rcvSize; @@ -220,11 +220,11 @@ int WiFiUDP::read(unsigned char* buf, size_t size) // sizeof(size_t) is architecture dependent // but we need a 16 bit data type here uint16_t size_tmp = available(); - + if (size_tmp == 0) { return -1; } - + if (size < size_tmp) { size_tmp = size; } diff --git a/src/WiFiUdp.h b/src/WiFiUdp.h index 039b4ac3..5f4f12fd 100644 --- a/src/WiFiUdp.h +++ b/src/WiFiUdp.h @@ -42,7 +42,7 @@ class WiFiUDP : public UDP { virtual void stop(); // Finish with the UDP socket // Sending UDP packets - + // Start building up a packet to send to the remote host specific in ip and port // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port virtual int beginPacket(IPAddress ip, uint16_t port); @@ -56,7 +56,7 @@ class WiFiUDP : public UDP { virtual size_t write(uint8_t); // Write size bytes from buffer into the packet virtual size_t write(const uint8_t *buffer, size_t size); - + using Print::write; // Start processing the next available incoming packet From 6b0690ca3009da9efecb3d19fc4c46d4b1d166d6 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Mon, 14 Mar 2016 10:53:29 -0400 Subject: [PATCH 3/5] Rework MDNS code to run with WiFi101 --- .../MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino | 55 +++++-------------- keywords.txt | 1 + ...Adafruit_WINC1500MDNS.cpp => WiFiMdns.cpp} | 14 ++--- src/{Adafruit_WINC1500MDNS.h => WiFiMdns.h} | 15 +++-- 4 files changed, 30 insertions(+), 55 deletions(-) rename src/{Adafruit_WINC1500MDNS.cpp => WiFiMdns.cpp} (96%) rename src/{Adafruit_WINC1500MDNS.h => WiFiMdns.h} (83%) diff --git a/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino b/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino index 639377e2..935077aa 100644 --- a/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino +++ b/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino @@ -2,15 +2,15 @@ MDNS WiFi Web Server A simple web server that shows the value of the analog input pins, - and exposes itself on the MDNS name 'winc1500.local'. + and exposes itself on the MDNS name 'wifi101.local'. On Linux (like Ubuntu 15.04) or OSX you can access the web page - on the device in a browser at 'http://winc1500.local/'. + on the device in a browser at 'http://wifi101.local/'. On Windows you'll first need to install the Bonjour Printer Services from: https://support.apple.com/kb/dl999?locale=en_US - Then you can access the device in a browser at 'http://winc1500.local/'. + Then you can access the device in a browser at 'http://wifi101.local/'. This example is written for a network using WPA encryption. For WEP or WPA, change the Wifi.begin() call accordingly. @@ -29,51 +29,26 @@ */ #include -#include -#include - -// Define the MDNS name that the board will respond to: -#define MDNS_NAME "winc1500" -// Note that the actual MDNS name will have '.local' after -// the name above, so "winc1500" will be accessible on -// the MDNS name "winc1500.local". - -// Define the WINC1500 board connections below. -// If you're following the Adafruit WINC1500 board -// guide you don't need to modify these: -#define WINC_CS 8 -#define WINC_IRQ 7 -#define WINC_RST 4 -#define WINC_EN 2 // or, tie EN to VCC and comment this out -// The SPI pins of the WINC1500 (SCK, MOSI, MISO) should be -// connected to the hardware SPI port of the Arduino. -// On an Uno or compatible these are SCK = #13, MISO = #12, MOSI = #11. -// On an Arduino Zero use the 6-pin ICSP header, see: -// https://www.arduino.cc/en/Reference/SPI - -// Setup the WINC1500 connection with the pins above and the default hardware SPI. -Adafruit_WINC1500 WiFi(WINC_CS, WINC_IRQ, WINC_RST); - -// Or just use hardware SPI (SCK/MOSI/MISO) and defaults, SS -> #10, INT -> #7, RST -> #5, EN -> 3-5V -//Adafruit_WINC1500 WiFi; +#include +#include char ssid[] = "yourNetwork"; // your network SSID (name) char pass[] = "secretPassword"; // your network password int keyIndex = 0; // your network key Index number (needed only for WEP) +char mdnsName[] = "wifi101"; // the MDNS name that the board will respond to +// Note that the actual MDNS name will have '.local' after +// the name above, so "wifi101" will be accessible on +// the MDNS name "wifi101.local". + int status = WL_IDLE_STATUS; // Create a MDNS responder to listen and respond to MDNS name requests. -MDNSResponder mdns(&WiFi); // Need to pass in a reference to the WiFi class above. +MDNSResponder mdns; -Adafruit_WINC1500Server server(80); +WiFiServer server(80); void setup() { -#ifdef WINC_EN - pinMode(WINC_EN, OUTPUT); - digitalWrite(WINC_EN, HIGH); -#endif - //Initialize serial and wait for port to open: Serial.begin(9600); while (!Serial) { @@ -105,13 +80,13 @@ void setup() { // Setup the MDNS responder to listen to the configured name. // NOTE: You _must_ call this _after_ connecting to the WiFi network and // being assigned an IP address. - if (!mdns.begin(MDNS_NAME)) { + if (!mdns.begin(mdnsName)) { Serial.println("Failed to start MDNS responder!"); while(1); } Serial.print("Server listening at http://"); - Serial.print(MDNS_NAME); + Serial.print(mdnsName); Serial.println(".local/"); } @@ -122,7 +97,7 @@ void loop() { mdns.update(); // listen for incoming clients - Adafruit_WINC1500Client client = server.available(); + WiFiClient client = server.available(); if (client) { Serial.println("new client"); // an http request ends with a blank line diff --git a/keywords.txt b/keywords.txt index 422b121c..2fae3650 100755 --- a/keywords.txt +++ b/keywords.txt @@ -40,6 +40,7 @@ getSocket KEYWORD2 WiFiClient KEYWORD2 WiFiServer KEYWORD2 WiFiSSLClient KEYWORD2 +WifiMdns KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/src/Adafruit_WINC1500MDNS.cpp b/src/WiFiMdns.cpp similarity index 96% rename from src/Adafruit_WINC1500MDNS.cpp rename to src/WiFiMdns.cpp index cbf4a09d..9649954b 100644 --- a/src/Adafruit_WINC1500MDNS.cpp +++ b/src/WiFiMdns.cpp @@ -20,8 +20,9 @@ // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + #include "Arduino.h" -#include "Adafruit_WINC1500MDNS.h" +#include "WiFiMdns.h" // Important RFC's for reference: // - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt @@ -36,14 +37,13 @@ #define IP_OFFSET 10 -MDNSResponder::MDNSResponder(Adafruit_WINC1500* wifi) +MDNSResponder::MDNSResponder() : _expected(NULL) , _expectedLen(0) + , _index(0) , _response(NULL) , _responseLen(0) - , _index(0) , _mdnsSocket() - , _wifi(wifi) { } MDNSResponder::~MDNSResponder() { @@ -74,7 +74,7 @@ bool MDNSResponder::begin(const char* domain, uint32_t ttlSeconds) } _expected[0] = (uint8_t)n; // Copy in domain characters as lowercase - for (int i = 0; i < n; ++i) { + for (unsigned int i = 0; i < n; ++i) { _expected[1+i] = tolower(domain[i]); } // Values for: @@ -134,7 +134,7 @@ bool MDNSResponder::begin(const char* domain, uint32_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 = _wifi->localIP(); + uint32_t ipAddress = WiFi.localIP(); records[IP_OFFSET] = (uint8_t) ipAddress; records[IP_OFFSET + 1] = (uint8_t)(ipAddress >> 8); records[IP_OFFSET + 2] = (uint8_t)(ipAddress >> 16); @@ -142,7 +142,7 @@ bool MDNSResponder::begin(const char* domain, uint32_t ttlSeconds) // Open the MDNS UDP listening socket on port 5353 with multicast address // 224.0.0.251 (0xE00000FB) - if (!_mdnsSocket.begin(5353, 0xE00000FB)) { + if (!_mdnsSocket.beginMulti(IPAddress(224, 0, 0, 251), 5353)) { return false; } diff --git a/src/Adafruit_WINC1500MDNS.h b/src/WiFiMdns.h similarity index 83% rename from src/Adafruit_WINC1500MDNS.h rename to src/WiFiMdns.h index f538bf52..e2620c7f 100644 --- a/src/Adafruit_WINC1500MDNS.h +++ b/src/WiFiMdns.h @@ -20,15 +20,16 @@ // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -#ifndef ADAFRUIT_WINC1500MDNS_H -#define ADAFRUIT_WINC1500MDNS_H -#include "Adafruit_WINC1500.h" -#include "Adafruit_WINC1500Udp.h" +#ifndef WIFIMDNS_H +#define WIFIMDNS_H + +#include "WiFi101.h" +#include "WiFiUdp.h" class MDNSResponder { public: - MDNSResponder(Adafruit_WINC1500* wifi); + MDNSResponder(); ~MDNSResponder(); bool begin(const char* domain, uint32_t ttlSeconds = 3600); void update(); @@ -46,9 +47,7 @@ class MDNSResponder { uint8_t* _response; int _responseLen; // UDP socket for receiving/sending MDNS data. - Adafruit_WINC1500UDP _mdnsSocket; - // Reference to WINC1500 wifi object, used to get IP address. - Adafruit_WINC1500* _wifi; + WiFiUDP _mdnsSocket; }; #endif From 53a9ecd433027708b487d05a7d658eab6699c06b Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Mon, 14 Mar 2016 16:20:10 -0400 Subject: [PATCH 4/5] Rework MDNS parsing to support longer domains and use progmem --- .../MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino | 2 +- keywords.txt | 1 + src/WiFiMdns.cpp | 290 +++++++++--------- src/WiFiMdns.h | 26 +- 4 files changed, 165 insertions(+), 154 deletions(-) diff --git a/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino b/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino index 935077aa..5bf9912d 100644 --- a/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino +++ b/examples/MDNS_WiFiWebServer/MDNS_WiFiWebServer.ino @@ -94,7 +94,7 @@ void setup() { void loop() { // Call the update() function on the MDNS responder every loop iteration to // make sure it can detect and respond to name requests. - mdns.update(); + mdns.poll(); // listen for incoming clients WiFiClient client = server.available(); diff --git a/keywords.txt b/keywords.txt index 2fae3650..c281b294 100755 --- a/keywords.txt +++ b/keywords.txt @@ -37,6 +37,7 @@ RSSI KEYWORD2 encryptionType KEYWORD2 getResult KEYWORD2 getSocket KEYWORD2 +poll KEYWORD2 WiFiClient KEYWORD2 WiFiServer KEYWORD2 WiFiSSLClient KEYWORD2 diff --git a/src/WiFiMdns.cpp b/src/WiFiMdns.cpp index 9649954b..464b1fff 100644 --- a/src/WiFiMdns.cpp +++ b/src/WiFiMdns.cpp @@ -21,6 +21,8 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#include + #include "Arduino.h" #include "WiFiMdns.h" @@ -28,170 +30,180 @@ // - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt // - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt -#define READ_BUFFER_SIZE 20 #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 +const uint8_t expectedRequestHeader[HEADER_SIZE] PROGMEM = { + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00 +}; -MDNSResponder::MDNSResponder() - : _expected(NULL) - , _expectedLen(0) - , _index(0) - , _response(NULL) - , _responseLen(0) - , _mdnsSocket() -{ } +const uint8_t responseHeader[] PROGMEM = { + 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 +}; -MDNSResponder::~MDNSResponder() { - if (_expected != NULL) { - free(_expected); - } - if (_response != NULL) { - free(_response); - } +// Generate positive response for IPV4 address +const uint8_t aRecord[] PROGMEM = { + 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[] PROGMEM = { + 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 +}; + +const uint8_t domain[] PROGMEM = { 'l', 'o', 'c', 'a', 'l' }; + +MDNSResponder::MDNSResponder() : + expectedRequestLength(0) +{ } -bool MDNSResponder::begin(const char* domain, uint32_t ttlSeconds) +MDNSResponder::~MDNSResponder() { - // 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 != NULL) { - free(_expected); - } - _expected = (uint8_t*) malloc(_expectedLen); - if (_expected == NULL) { - return false; - } - _expected[0] = (uint8_t)n; - // Copy in domain characters as lowercase - for (unsigned 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) - 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. - 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 - 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) - 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 != NULL) { - free(_response); - } - _response = (uint8_t*) malloc(_responseLen); - if (_response == NULL) { +} + +bool MDNSResponder::begin(const char* _name, uint32_t _ttlSeconds) +{ + int nameLength = strlen(_name); + + if (nameLength > 255) { + // Can only handle domains that are upto 255 chars in length. + expectedRequestLength = 0; return false; } - // 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 = WiFi.localIP(); - records[IP_OFFSET] = (uint8_t) ipAddress; - records[IP_OFFSET + 1] = (uint8_t)(ipAddress >> 8); - records[IP_OFFSET + 2] = (uint8_t)(ipAddress >> 16); - records[IP_OFFSET + 3] = (uint8_t)(ipAddress >> 24); + + name = _name; + ttlSeconds = _ttlSeconds; + + name.toLowerCase(); + expectedRequestLength = HEADER_SIZE + 1 + nameLength + 1 + sizeof(domain) + 5; // Open the MDNS UDP listening socket on port 5353 with multicast address // 224.0.0.251 (0xE00000FB) - if (!_mdnsSocket.beginMulti(IPAddress(224, 0, 0, 251), 5353)) { + if (!udpSocket.beginMulti(IPAddress(224, 0, 0, 251), 5353)) { return false; } return true; } -void MDNSResponder::update() { - // Check if there's data to read from the UDP socket. - int available = _mdnsSocket.parsePacket(); - // Stop processing if no data is available. - if (available <= 0) { - return; - } - // Otherwise there is data to read so grab it all and parse the data. - uint8_t buffer[READ_BUFFER_SIZE]; - int n = _mdnsSocket.read((unsigned char*)&buffer, sizeof(buffer)); - if (n < 1) { - // Error getting data. - return; +void MDNSResponder::poll() +{ + if (parseRequest()) { + replyToRequest(); } - // Look for domain name in request and respond with canned response if found. - for (int i = 0; i < n; ++i) { - uint8_t ch = tolower(buffer[i]); - // 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. - _broadcastResponse(); - _index = 0; +} + +bool MDNSResponder::parseRequest() +{ + if (udpSocket.parsePacket()) { + // check if parsed packet matches expected request length + if (udpSocket.available() != expectedRequestLength) { + // it does not, read the full packet in and drop data + while(udpSocket.available()) { + udpSocket.read(); } + + return false; } - 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; + + // read packet + uint8_t request[expectedRequestLength]; + udpSocket.read(request, expectedRequestLength); + + // parse request + uint8_t requestNameLength = request[HEADER_SIZE]; + uint8_t* requestName = &request[HEADER_SIZE + 1]; + uint8_t requestDomainLength = request[HEADER_SIZE + 1 + requestNameLength]; + uint8_t* requestDomain = &request[HEADER_SIZE + 1 + requestNameLength + 1]; + uint16_t requestQtype; + uint16_t requestQclass; + + memcpy(&requestQtype, &request[expectedRequestLength - 4], sizeof(requestQtype)); + memcpy(&requestQclass, &request[expectedRequestLength - 2], sizeof(requestQclass)); + + requestQtype = _ntohs(requestQtype); + requestQclass = _ntohs(requestQclass); + + // compare request + if (memcmp_P(request, expectedRequestHeader, HEADER_SIZE) == 0 && // request header match + requestNameLength == name.length() && // name length match + strncasecmp(name.c_str(), (char*)requestName, requestNameLength) == 0 && // name match + requestDomainLength == sizeof(domain) && // domain length match + memcmp_P(requestDomain, domain, requestDomainLength) == 0 && // suffix match + requestQtype == 0x0001 && // request QType match + (requestQclass == 0x0001 || requestQclass == 0x8001) ) { // request QClass match + + return true; } } + + return false; } -void MDNSResponder::_broadcastResponse() { - // Send a MDNS name query response for this device. - // Use the MDNS multicast address 224.0.0.251 port 5353. - _mdnsSocket.beginPacket(IPAddress(224, 0, 0, 251), 5353); - _mdnsSocket.write(_response, _responseLen); - _mdnsSocket.endPacket(); +void MDNSResponder::replyToRequest() +{ + int nameLength = name.length(); + int domainLength = sizeof(domain); + uint32_t ipAddress = WiFi.localIP(); + uint32_t ttl = _htonl(ttlSeconds); + + int responseSize = sizeof(responseHeader) + 1 + nameLength + 1 + domainLength + 1 + sizeof(aRecord) + sizeof(nsecRecord); + uint8_t response[responseSize]; + uint8_t* r = response; + + // copy header + memcpy_P(r, responseHeader, sizeof(responseHeader)); + r += sizeof(responseHeader); + + // copy name + *r = nameLength; + memcpy(r + 1, name.c_str(), nameLength); + r += (1 + nameLength); + + // copy domain + *r = domainLength; + memcpy_P(r + 1, domain, domainLength); + r += (1 + domainLength); + + // terminator + *r = 0x00; + r++; + + // copy A record + memcpy_P(r, aRecord, sizeof(aRecord)); + memcpy(r + TTL_OFFSET, &ttl, sizeof(ttl)); // replace TTL value + memcpy(r + IP_OFFSET, &ipAddress, sizeof(ipAddress)); // replace IP address value + r += sizeof(aRecord); + + // copy NSEC record + memcpy_P(r, nsecRecord, sizeof(nsecRecord)); + r += sizeof(nsecRecord); + + udpSocket.beginPacket(IPAddress(224, 0, 0, 251), 5353); + udpSocket.write(response, responseSize); + udpSocket.endPacket(); } diff --git a/src/WiFiMdns.h b/src/WiFiMdns.h index e2620c7f..4f069aa5 100644 --- a/src/WiFiMdns.h +++ b/src/WiFiMdns.h @@ -31,23 +31,21 @@ class MDNSResponder { public: MDNSResponder(); ~MDNSResponder(); - bool begin(const char* domain, uint32_t ttlSeconds = 3600); - void update(); + bool begin(const char* _name, uint32_t _ttlSeconds = 3600); + void poll(); private: - void _broadcastResponse(); - - // Expected query values - static uint8_t _queryHeader[]; - uint8_t* _expected; - int _expectedLen; - // Current parsing state - int _index; - // Response data - uint8_t* _response; - int _responseLen; + bool parseRequest(); + void replyToRequest(); + +private: + String name; + uint32_t ttlSeconds; + + int expectedRequestLength; + // UDP socket for receiving/sending MDNS data. - WiFiUDP _mdnsSocket; + WiFiUDP udpSocket; }; #endif From 68509e7798240cd64cc32da782d87f255b3c7738 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Mon, 14 Mar 2016 16:25:21 -0400 Subject: [PATCH 5/5] Simplify requestQclass comparison --- src/WiFiMdns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WiFiMdns.cpp b/src/WiFiMdns.cpp index 464b1fff..ceca86d8 100644 --- a/src/WiFiMdns.cpp +++ b/src/WiFiMdns.cpp @@ -155,7 +155,7 @@ bool MDNSResponder::parseRequest() requestDomainLength == sizeof(domain) && // domain length match memcmp_P(requestDomain, domain, requestDomainLength) == 0 && // suffix match requestQtype == 0x0001 && // request QType match - (requestQclass == 0x0001 || requestQclass == 0x8001) ) { // request QClass match + requestQclass == 0x0001) { // request QClass match return true; }