publish libraries to avoid users to have to download them one by one
This commit is contained in:
1 parent
93c075e880
commit
f0546fab8c
396 files changed
+536928
No files matched your search
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) [2015] [Duncan Law]
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,91 @@
|
||||
# esp8266_mdns
|
||||
mDNS queries and responses on esp8266.
|
||||
Or to describe it another way: An mDNS Client or Bonjour Client library for the esp8266.
|
||||
|
||||
This library aims to do the following:
|
||||
1. Give access to incoming mDNS packets and decode Question and Answer Records for commonly used record types.
|
||||
2. Allow Question and Answer Records for commonly used record types to be sent.
|
||||
|
||||
Future goals:
|
||||
1. Dynamic buffer paging. Currently one page is read from the network. If the mDNS packet is larger than that page size, any responses in the remainder are lost. (See MAX_PACKET_SIZE in mdns.h.)
|
||||
2. Automatic replies to incoming Questions.
|
||||
3. Automatic retries when sending packets according to rfc6762.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
- An Espressif [ESP8266](http://www.esp8266.com/) WiFi enabled SOC.
|
||||
- The [ESP8266 Arduino](https://github.com/esp8266/Arduino) environment.
|
||||
- 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
|
||||
-----
|
||||
Find information on how to add a library to your Arduino IDE [here](https://www.arduino.cc/en/Guide/Libraries).
|
||||
|
||||
The file [mdns.h](https://github.com/mrdunk/esp8266_mdns/blob/master/mdns.h) is well commented. Try looking there for information on specific methods.
|
||||
|
||||
To add a simple mDNS listener to an Aruino sketch which will display all mDNS packets over the serial console try the following:
|
||||
|
||||
```
|
||||
// This sketch will display mDNS (multicast DNS) data seen on the network.
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include "mdns.h"
|
||||
|
||||
// When an mDNS packet gets parsed this optional callback gets called.
|
||||
void packetCallback(const mdns::MDns* packet){
|
||||
packet->Display();
|
||||
packet->DisplayRawPacket();
|
||||
}
|
||||
|
||||
// When an mDNS packet gets parsed this optional callback gets called once per Query.
|
||||
// See mdns.h for definition of mdns::Query.
|
||||
void queryCallback(const mdns::Query* query){
|
||||
query->Display();
|
||||
}
|
||||
|
||||
// When an mDNS packet gets parsed this optional callback gets called once per Query.
|
||||
// See mdns.h for definition of mdns::Answer.
|
||||
void answerCallback(const mdns::Answer* answer){
|
||||
answer->Display();
|
||||
}
|
||||
|
||||
// Initialise MDns.
|
||||
// If you don't want the optional callbacks, just provide a NULL pointer as the callback.
|
||||
mdns::MDns my_mdns(packetCallback, queryCallback, answerCallback);
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
|
||||
// setting up Station AP
|
||||
WiFi.begin("your_wifi_ssid", "your_wifi_password");
|
||||
|
||||
// Wait for connect to AP
|
||||
int tries = 0;
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
tries++;
|
||||
if (tries > 30) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
my_mdns.loop();
|
||||
}
|
||||
```
|
||||
|
||||
A more complete example which sends an mDNS Question and parses Answers is available in esp8266_mdns/examples/mdns_test/ .
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
Run [Wireshark](https://www.wireshark.org/) on a machine connected to your wireless network to confirm what is actually in flight.
|
||||
The following filter will return only mDNS packets: ```udp.port == 5353``` .
|
||||
Any mDNS packets seen by Wireshark should also appear on the ESP8266 Serial console.
|
||||
@@ -0,0 +1,5 @@
|
||||
# esp8266_mdns test program.
|
||||
This is a sample program using the esp8266_mdns library.
|
||||
It queries the network for hosts providing the service defined in QUESTION_SERVICE.
|
||||
Any answers are parsed and stored, hopefully providing you with the port and ipv4 network address of all hosts on the network providing the service.
|
||||
See https://github.com/mrdunk/esp8266_mdns for more information on the library and mDNS in general.
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This sketch will query the network for hosts providing the service defined by
|
||||
* QUESTION_SERVICE and will parse any replies with the aim of saving port and
|
||||
* network address in the hosts array.
|
||||
*/
|
||||
|
||||
|
||||
#include <mdns.h>
|
||||
|
||||
#include "secrets.h" // Contains the following:
|
||||
// const char* ssid = "Get off my wlan"; // your network SSID (name)
|
||||
// const char* pass = "secretwlanpass"; // your network password
|
||||
|
||||
|
||||
#define QUESTION_SERVICE "_mqtt._tcp.local"
|
||||
|
||||
// Make this value as large as available ram allows.
|
||||
#define MAX_MDNS_PACKET_SIZE 512
|
||||
|
||||
#define MAX_HOSTS 4
|
||||
#define HOSTS_SERVICE_NAME 0
|
||||
#define HOSTS_PORT 1
|
||||
#define HOSTS_HOST_NAME 2
|
||||
#define HOSTS_ADDRESS 3
|
||||
String hosts[MAX_HOSTS][4]; // Array containing information about hosts received over mDNS.
|
||||
|
||||
|
||||
// When an mDNS packet gets parsed this callback gets called once per Query.
|
||||
// See mdns.h for definition of mdns::Query.
|
||||
void answerCallback(const mdns::Answer* answer) {
|
||||
|
||||
// A typical PTR record matches service to a human readable name.
|
||||
// eg:
|
||||
// service: _mqtt._tcp.local
|
||||
// name: Mosquitto MQTT server on twinkle.local
|
||||
if (answer->rrtype == MDNS_TYPE_PTR and strstr(answer->name_buffer, QUESTION_SERVICE) != 0) {
|
||||
unsigned int i = 0;
|
||||
for (; i < MAX_HOSTS; ++i) {
|
||||
if (hosts[i][HOSTS_SERVICE_NAME] == answer->rdata_buffer) {
|
||||
// Already in hosts[][].
|
||||
break;
|
||||
}
|
||||
if (hosts[i][HOSTS_SERVICE_NAME] == "") {
|
||||
// This hosts[][] entry is still empty.
|
||||
hosts[i][HOSTS_SERVICE_NAME] = answer->rdata_buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MAX_HOSTS) {
|
||||
Serial.print(" ** ERROR ** No space in buffer for ");
|
||||
Serial.print('"');
|
||||
Serial.print(answer->name_buffer);
|
||||
Serial.print('"');
|
||||
Serial.print(" : ");
|
||||
Serial.print('"');
|
||||
Serial.println(answer->rdata_buffer);
|
||||
Serial.print('"');
|
||||
}
|
||||
}
|
||||
|
||||
// A typical SRV record matches a human readable name to port and FQDN info.
|
||||
// eg:
|
||||
// name: Mosquitto MQTT server on twinkle.local
|
||||
// data: p=0;w=0;port=1883;host=twinkle.local
|
||||
if (answer->rrtype == MDNS_TYPE_SRV) {
|
||||
unsigned int i = 0;
|
||||
for (; i < MAX_HOSTS; ++i) {
|
||||
if (hosts[i][HOSTS_SERVICE_NAME] == answer->name_buffer) {
|
||||
// This hosts entry matches the name of the host we are looking for
|
||||
// so parse data for port and hostname.
|
||||
char* port_start = strstr(answer->rdata_buffer, "port=");
|
||||
if (port_start) {
|
||||
port_start += 5;
|
||||
char* port_end = strchr(port_start, ';');
|
||||
char port[1 + port_end - port_start];
|
||||
strncpy(port, port_start, port_end - port_start);
|
||||
port[port_end - port_start] = '\0';
|
||||
|
||||
if (port_end) {
|
||||
char* host_start = strstr(port_end, "host=");
|
||||
if (host_start) {
|
||||
host_start += 5;
|
||||
hosts[i][HOSTS_PORT] = port;
|
||||
hosts[i][HOSTS_HOST_NAME] = host_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MAX_HOSTS) {
|
||||
Serial.print(" Did not find ");
|
||||
Serial.print('"');
|
||||
Serial.print(answer->name_buffer);
|
||||
Serial.print('"');
|
||||
Serial.println(" in hosts buffer.");
|
||||
}
|
||||
}
|
||||
|
||||
// A typical A record matches an FQDN to network ipv4 address.
|
||||
// eg:
|
||||
// name: twinkle.local
|
||||
// address: 192.168.192.9
|
||||
if (answer->rrtype == MDNS_TYPE_A) {
|
||||
int i = 0;
|
||||
for (; i < MAX_HOSTS; ++i) {
|
||||
if (hosts[i][HOSTS_HOST_NAME] == answer->name_buffer) {
|
||||
hosts[i][HOSTS_ADDRESS] = answer->rdata_buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MAX_HOSTS) {
|
||||
Serial.print(" Did not find ");
|
||||
Serial.print('"');
|
||||
Serial.print(answer->name_buffer);
|
||||
Serial.print('"');
|
||||
Serial.println(" in hosts buffer.");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
for (int i = 0; i < MAX_HOSTS; ++i) {
|
||||
if (hosts[i][HOSTS_SERVICE_NAME] != "") {
|
||||
Serial.print("> ");
|
||||
Serial.print(hosts[i][HOSTS_SERVICE_NAME]);
|
||||
Serial.print(" ");
|
||||
Serial.print(hosts[i][HOSTS_PORT]);
|
||||
Serial.print(" ");
|
||||
Serial.print(hosts[i][HOSTS_HOST_NAME]);
|
||||
Serial.print(" ");
|
||||
Serial.println(hosts[i][HOSTS_ADDRESS]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// buffer can be used bu other processes that need a large chunk of memory.
|
||||
byte buffer[MAX_MDNS_PACKET_SIZE];
|
||||
mdns::MDns my_mdns(NULL, NULL, answerCallback, buffer, MAX_MDNS_PACKET_SIZE);
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
|
||||
// setting up Station AP
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
// Wait for connect to AP
|
||||
Serial.print("[Connecting]");
|
||||
Serial.print(ssid);
|
||||
int tries = 0;
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
tries++;
|
||||
if (tries > 30) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
printWifiStatus();
|
||||
|
||||
Serial.println("Connected to wifi");
|
||||
|
||||
|
||||
// Query for all host information for a paticular service. ("_mqtt" in this case.)
|
||||
my_mdns.Clear();
|
||||
struct mdns::Query query_mqtt;
|
||||
strncpy(query_mqtt.qname_buffer, QUESTION_SERVICE, MAX_MDNS_NAME_LEN);
|
||||
query_mqtt.qtype = MDNS_TYPE_PTR;
|
||||
query_mqtt.qclass = 1; // "INternet"
|
||||
query_mqtt.unicast_response = 0;
|
||||
my_mdns.AddQuery(query_mqtt);
|
||||
my_mdns.Send();
|
||||
|
||||
/*
|
||||
// Query for all service types on network.
|
||||
my_mdns.Clear();
|
||||
struct mdns::Query query_services;
|
||||
strncpy(query_services.qname_buffer, "_services._dns-sd._udp.local", MAX_MDNS_NAME_LEN);
|
||||
query_services.qtype = MDNS_TYPE_PTR;
|
||||
query_services.qclass = 1; // "INternet"
|
||||
query_services.unicast_response = 0;
|
||||
my_mdns.AddQuery(query_services);
|
||||
my_mdns.Send();*/
|
||||
|
||||
}
|
||||
|
||||
unsigned int last_packet_count = 0;
|
||||
void loop()
|
||||
{
|
||||
my_mdns.loop();
|
||||
|
||||
#ifdef DEBUG_STATISTICS
|
||||
// Give feedback on the percentage of incoming mDNS packets that fitted in buffer.
|
||||
// Useful for tuning the buffer size to make best use of available memory.
|
||||
if(last_packet_count != my_mdns.packet_count && my_mdns.packet_count != 0){
|
||||
last_packet_count = my_mdns.packet_count;
|
||||
Serial.print("mDNS decode success rate: ");
|
||||
Serial.print(100 - (100 * my_mdns.buffer_size_fail / my_mdns.packet_count));
|
||||
Serial.print("%\nLargest packet size: ");
|
||||
Serial.println(my_mdns.largest_packet_seen);
|
||||
}
|
||||
#endif
|
||||
|
||||
// mDNS not using buffer outside my_mdns.loop() so it can be used for other tasks.
|
||||
strncpy((char*)buffer,
|
||||
"<html><head>Some webpage that needs a large buffer</head>"
|
||||
"<body>big content...</body></html>",
|
||||
MAX_MDNS_PACKET_SIZE);
|
||||
// Display buffer here....
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This sketch will display mDNS (multicast DNS) data seen on the network.
|
||||
*/
|
||||
|
||||
|
||||
#include "mdns.h"
|
||||
|
||||
|
||||
// When an mDNS packet gets parsed this callback gets called.
|
||||
void packetCallback(const mdns::MDns* packet){
|
||||
packet->Display();
|
||||
packet->DisplayRawPacket();
|
||||
}
|
||||
|
||||
// When an mDNS packet gets parsed this callback gets called once per Query.
|
||||
// See mdns.h for definition of mdns::Query.
|
||||
void queryCallback(const mdns::Query* query){
|
||||
query->Display();
|
||||
}
|
||||
|
||||
// When an mDNS packet gets parsed this callback gets called once per Query.
|
||||
// See mdns.h for definition of mdns::Answer.
|
||||
void answerCallback(const mdns::Answer* answer){
|
||||
answer->Display();
|
||||
}
|
||||
|
||||
// Initialise MDns. If you don't want the optioanl callbacks, just provide a NULL pointer.
|
||||
mdns::MDns my_mdns(packetCallback, queryCallback, answerCallback);
|
||||
|
||||
void setup() {
|
||||
// Open serial communications and wait for port to open:
|
||||
Serial.begin(115200);
|
||||
|
||||
// setting up Station AP
|
||||
WiFi.begin("your_WiFI_SSID", "your_WiFi_password");
|
||||
|
||||
// Wait for connect to AP
|
||||
int tries = 0;
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
tries++;
|
||||
if (tries > 30) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
printWifiStatus();
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
unsigned int last_packet_count = 0;
|
||||
void loop() {
|
||||
my_mdns.loop();
|
||||
|
||||
#ifdef DEBUG_STATISTICS
|
||||
// Give feedback on the percentage of incoming mDNS packets that fitted in buffer.
|
||||
// Useful for tuning the buffer size to make best use of available memory.
|
||||
if(last_packet_count != my_mdns.packet_count && my_mdns.packet_count != 0){
|
||||
last_packet_count = my_mdns.packet_count;
|
||||
Serial.print("mDNS decode success rate: ");
|
||||
Serial.print(100 - (100 * my_mdns.buffer_size_fail / my_mdns.packet_count));
|
||||
Serial.print("%\nLargest packet size: ");
|
||||
Serial.println(my_mdns.largest_packet_seen);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void printWifiStatus() {
|
||||
// print the SSID of the network you're attached to:
|
||||
Serial.println();
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "esp8266_mdns",
|
||||
"keywords": "esp8266, mdns, Zeroconf, Bonjour, Avahi",
|
||||
"description": "mDNS queries and responses on esp8266. Or to describe it another way: An mDNS Client or Bonjour Client library for the esp8266.",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/mrdunk/esp8266_mdns.git"
|
||||
},
|
||||
"frameworks": "arduino",
|
||||
"platforms": "espressif"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name=esp8266_mdns
|
||||
version=1.1.7
|
||||
author=mrdunk
|
||||
maintainer=mrdunk
|
||||
sentence=mDNS queries and responses on esp8266.
|
||||
paragraph=Or to describe it another way: An mDNS Client or Bonjour Client library for the esp8266.
|
||||
category=Communication
|
||||
url=https://github.com/mrdunk/esp8266_mdns
|
||||
architectures=esp8266
|
||||
@@ -0,0 +1,627 @@
|
||||
#include <Arduino.h>
|
||||
#include "mdns.h"
|
||||
|
||||
|
||||
namespace mdns {
|
||||
|
||||
|
||||
// A UDP instance to let us send and receive packets over UDP.
|
||||
WiFiUDP Udp;
|
||||
|
||||
// Helper function to display formatted data.
|
||||
void PrintHex(const unsigned char data) {
|
||||
char tmp[2];
|
||||
sprintf(tmp, "%02X", data);
|
||||
Serial.print(tmp);
|
||||
Serial.print(" ");
|
||||
}
|
||||
|
||||
void MDns::startUdpMulticast(){
|
||||
Serial.println("Initializing Multicast.");
|
||||
Udp.beginMulticast(WiFi.localIP(), IPAddress(224, 0, 0, 251), MDNS_TARGET_PORT);
|
||||
}
|
||||
|
||||
bool MDns::loop() {
|
||||
data_size = Udp.parsePacket();
|
||||
if ( data_size > 12) {
|
||||
if(data_size > largest_packet_seen){
|
||||
largest_packet_seen = data_size;
|
||||
}
|
||||
#ifdef DEBUG_STATISTICS
|
||||
if(data_size > max_packet_size) {
|
||||
buffer_size_fail++;
|
||||
data_size = max_packet_size;
|
||||
}
|
||||
packet_count++;
|
||||
#endif
|
||||
|
||||
// We've received a packet which is long enough to contain useful data so
|
||||
// read the data from it.
|
||||
Udp.read(data_buffer, data_size); // read the packet into the buffer
|
||||
// data_buffer[0] and data_buffer[1] contain the Query ID field which is unused in mDNS.
|
||||
|
||||
// data_buffer[2] and data_buffer[3] are DNS flags which are mostly unused in mDNS.
|
||||
type = !(data_buffer[2] & 0b10000000); // If it's not a query, it's an answer.
|
||||
truncated = data_buffer[2] & 0b00000010; // If it's truncated we can expect more data soon so we should wait for additional records before deciding whether to respond.
|
||||
if (data_buffer[3] & 0b00001111) {
|
||||
// Non zero Response code implies error.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Number of incoming queries.
|
||||
query_count = (data_buffer[4] << 8) + data_buffer[5];
|
||||
|
||||
// Number of incoming answers.
|
||||
answer_count = (data_buffer[6] << 8) + data_buffer[7];
|
||||
|
||||
// Number of incoming Name Server resource records.
|
||||
ns_count = (data_buffer[8] << 8) + data_buffer[9];
|
||||
|
||||
// Number of incoming Additional resource records.
|
||||
ar_count = (data_buffer[10] << 8) + data_buffer[11];
|
||||
|
||||
if(p_packet_function_) {
|
||||
// Since a callback function has been registered, execute it.
|
||||
p_packet_function_(this);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Display();
|
||||
#endif // DEBUG_OUTPUT
|
||||
|
||||
// Start of Data section.
|
||||
buffer_pointer = 12;
|
||||
|
||||
for (unsigned int i_question = 0; i_question < query_count; i_question++) {
|
||||
Query query;
|
||||
Parse_Query(query);
|
||||
if (query.valid) {
|
||||
if (p_query_function_) {
|
||||
// Since a callback function has been registered, execute it.
|
||||
p_query_function_(&query);
|
||||
}
|
||||
}
|
||||
if(buffer_pointer >= data_size){
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG_OUTPUT
|
||||
query.Display();
|
||||
#endif // DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
for (unsigned int i_answer = 0; i_answer < (answer_count + ns_count + ar_count); i_answer++) {
|
||||
Answer answer;
|
||||
Parse_Answer(answer);
|
||||
if (answer.valid) {
|
||||
if (p_answer_function_) {
|
||||
// Since a callback function has been registered, execute it.
|
||||
p_answer_function_(&answer);
|
||||
}
|
||||
}
|
||||
if(buffer_pointer >= data_size){
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG_OUTPUT
|
||||
answer.Display();
|
||||
#endif // DEBUG_OUTPUT
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RAW
|
||||
DisplayRawPacket();
|
||||
#endif // DEBUG_RAW
|
||||
|
||||
return true;
|
||||
}
|
||||
return true; // Not enough data for a full packet to be waiting.
|
||||
}
|
||||
|
||||
void MDns::Clear() {
|
||||
data_buffer[0] = 0; // Query ID field which is unused in mDNS.
|
||||
data_buffer[1] = 0; // Query ID field which is unused in mDNS.
|
||||
data_buffer[2] = 0; // 0b00000000 for Query, 0b10000000 for Answer.
|
||||
data_buffer[3] = 0; // DNS flags which are mostly unused in mDNS.
|
||||
data_buffer[4] = 0; // Number of queries.
|
||||
data_buffer[5] = 0; // Number of queries.
|
||||
data_buffer[6] = 0; // Number of answers.
|
||||
data_buffer[7] = 0; // Number of answers.
|
||||
data_buffer[8] = 0; // Number of Server esource records.
|
||||
data_buffer[9] = 0; // Number of Server esource records.
|
||||
data_buffer[10] = 0; // Number of Additional resource records.
|
||||
data_buffer[11] = 0; // Number of Additional resource records.
|
||||
|
||||
data_size = 12;
|
||||
buffer_pointer = 12; // First byte of first Query/Record.
|
||||
type = 0;
|
||||
query_count = 0;
|
||||
answer_count = 0;
|
||||
ns_count = 0;
|
||||
ar_count = 0;
|
||||
}
|
||||
|
||||
unsigned int MDns::PopulateName(const char* name_buffer) {
|
||||
// TODO: This section does not match the full mDNS spec
|
||||
// as it does not re-use strings from previous queries.
|
||||
|
||||
unsigned int buffer_pointer_start = buffer_pointer;
|
||||
int word_start = 0, word_end = 0;
|
||||
while (true) {
|
||||
if (name_buffer[word_end] == '.' or name_buffer[word_end] == '\0') {
|
||||
const int word_length = word_end - word_start;
|
||||
if(buffer_pointer >= data_size){
|
||||
buffer_pointer = buffer_pointer_start;
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. MDns::PopulateName overran buffer.");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
data_buffer[buffer_pointer++] = (unsigned byte)word_length;
|
||||
for (int i = word_start; i < word_end; ++i) {
|
||||
if(buffer_pointer >= data_size){
|
||||
buffer_pointer = buffer_pointer_start;
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. MDns::PopulateName overran buffer.");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
data_buffer[buffer_pointer++] = name_buffer[i];
|
||||
}
|
||||
if(name_buffer[word_end] == '\0'){
|
||||
break;
|
||||
}
|
||||
word_end++; // Skip the '.' character.
|
||||
word_start = word_end;
|
||||
}
|
||||
word_end++;
|
||||
};
|
||||
|
||||
if(buffer_pointer >= data_size){
|
||||
buffer_pointer = buffer_pointer_start;
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. MDns::PopulateName overran buffer while finishing.");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
data_buffer[buffer_pointer++] = '\0'; // End of qname.
|
||||
|
||||
return buffer_pointer - buffer_pointer_start;
|
||||
}
|
||||
|
||||
bool MDns::AddQuery(const Query& query) {
|
||||
if (answer_count || ns_count || ar_count) {
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. Resource records included before Queries.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Buffer increased by length of qname_buffer + a preceding length + zero termination
|
||||
// + 4 bits of mDNS flags.
|
||||
data_size += strlen(query.qname_buffer) +6;
|
||||
|
||||
// Create DNS name buffer from qname.
|
||||
if(PopulateName(query.qname_buffer) == 0 || buffer_pointer +4 > data_size){
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. MDns::AddQuery overran expected buffer space.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
// The rest of the flags.
|
||||
data_buffer[buffer_pointer++] = (query.qtype & 0xFF00) >> 8;
|
||||
data_buffer[buffer_pointer++] = query.qtype & 0xFF;
|
||||
unsigned int qclass = 0;
|
||||
if (query.unicast_response) {
|
||||
qclass = 0b1000000000000000;
|
||||
}
|
||||
qclass += query.qclass;
|
||||
data_buffer[buffer_pointer++] = (qclass & 0xFF00) >> 8;
|
||||
data_buffer[buffer_pointer++] = qclass & 0xFF;
|
||||
data_size = buffer_pointer;
|
||||
|
||||
// Since the data fitted in the buffer, it's ok to update the header.
|
||||
data_buffer[2] = 0; // 0b00000000 for Query, 0b10000000 for Answer.
|
||||
type = 1;
|
||||
++query_count;
|
||||
data_buffer[4] = (query_count & 0xFF00) >> 8;
|
||||
data_buffer[5] = query_count & 0xFF;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MDns::AddAnswer(const Answer& answer) {
|
||||
if (ns_count || ar_count) {
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. NS or AR records added before Answer records");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
data_size += strlen(answer.name_buffer) +12;
|
||||
|
||||
// Create DNS name buffer from name.
|
||||
if(PopulateName(answer.name_buffer) == 0 || buffer_pointer +10 > data_size){
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.println(" ERROR. MDns::AddAnswer over-ran expected buffer space.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
data_buffer[buffer_pointer++] = (answer.rrtype & 0xFF00) >> 8;
|
||||
data_buffer[buffer_pointer++] = answer.rrtype & 0xFF;
|
||||
|
||||
unsigned int rrclass = 0;
|
||||
if (answer.rrset) {
|
||||
rrclass = 0b1000000000000000;
|
||||
}
|
||||
rrclass += answer.rrclass;
|
||||
data_buffer[buffer_pointer++] = (rrclass & 0xFF00) >> 8;
|
||||
data_buffer[buffer_pointer++] = rrclass & 0xFF;
|
||||
|
||||
data_buffer[buffer_pointer++] = (answer.rrttl & 0xFF000000) >> 24;
|
||||
data_buffer[buffer_pointer++] = (answer.rrttl & 0xFF0000) >> 16;
|
||||
data_buffer[buffer_pointer++] = (answer.rrttl & 0xFF00) >> 8;
|
||||
data_buffer[buffer_pointer++] = (answer.rrttl & 0xFF);
|
||||
|
||||
const unsigned int rdata_len_p0 = buffer_pointer++;
|
||||
const unsigned int rdata_len_p1 = buffer_pointer++;
|
||||
unsigned int rdata_len = 0;
|
||||
|
||||
switch (answer.rrtype) {
|
||||
case MDNS_TYPE_A: // Returns a 32-bit IPv4 address
|
||||
if(buffer_pointer +4 >= data_size){ return false; }
|
||||
rdata_len = 4;
|
||||
data_buffer[buffer_pointer++] = answer.rdata_buffer[0];
|
||||
data_buffer[buffer_pointer++] = answer.rdata_buffer[1];
|
||||
data_buffer[buffer_pointer++] = answer.rdata_buffer[2];
|
||||
data_buffer[buffer_pointer++] = answer.rdata_buffer[3];
|
||||
break;
|
||||
case MDNS_TYPE_PTR: // Pointer to a canonical name.
|
||||
rdata_len = PopulateName(answer.rdata_buffer);
|
||||
if(buffer_pointer >= data_size){ return false; }
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_OUTPUT
|
||||
// TODO: Other record types.
|
||||
Serial.println(" **ERROR** Sending this record type not implemented yet.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
data_buffer[rdata_len_p0] = (rdata_len & 0xFF00) >> 8;
|
||||
data_buffer[rdata_len_p1] = rdata_len & 0xFF;
|
||||
|
||||
data_size = buffer_pointer;
|
||||
|
||||
// Since the data fitted in the buffer, it's ok to update the header.
|
||||
answer_count++;
|
||||
data_buffer[6] = (answer_count & 0xFF00) >> 8;
|
||||
data_buffer[7] = answer_count & 0xFF;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MDns::Send() const {
|
||||
Serial.println("Sending UDP multicast packet");
|
||||
|
||||
Udp.begin(MDNS_SOURCE_PORT);
|
||||
Udp.beginPacketMulticast(IPAddress(224, 0, 0, 251), MDNS_TARGET_PORT, WiFi.localIP(), MDNS_TTL);
|
||||
Udp.write(data_buffer, data_size);
|
||||
Udp.endPacket();
|
||||
}
|
||||
|
||||
void MDns::Display() const {
|
||||
Serial.println();
|
||||
Serial.print("Packet size: ");
|
||||
Serial.print(data_size);
|
||||
Serial.print(" ");
|
||||
Serial.println(data_size, HEX);
|
||||
Serial.print(" TYPE: ");
|
||||
Serial.print(type);
|
||||
Serial.print(" QUERY_COUNT: ");
|
||||
Serial.print(query_count);
|
||||
Serial.print(" ANSWER_COUNT: ");
|
||||
Serial.print(answer_count);
|
||||
Serial.print(" NS_COUNT: ");
|
||||
Serial.print(ns_count);
|
||||
Serial.print(" AR_COUNT: ");
|
||||
Serial.println(ar_count);
|
||||
}
|
||||
|
||||
void MDns::Parse_Query(Query& query) {
|
||||
#ifdef DEBUG_OUTPUT
|
||||
query.buffer_pointer = buffer_pointer;
|
||||
#endif
|
||||
|
||||
buffer_pointer = nameFromDnsPointer(query.qname_buffer, 0, MAX_MDNS_NAME_LEN,
|
||||
data_buffer, buffer_pointer);
|
||||
|
||||
byte qtype_0 = data_buffer[buffer_pointer++];
|
||||
byte qtype_1 = data_buffer[buffer_pointer++];
|
||||
byte qclass_0 = data_buffer[buffer_pointer++];
|
||||
byte qclass_1 = data_buffer[buffer_pointer++];
|
||||
|
||||
query.qtype = (qtype_0 << 8) + qtype_1;
|
||||
|
||||
query.unicast_response = (0b10000000 & qclass_0);
|
||||
query.qclass = ((qclass_0 & 0b01111111) << 8) + qclass_1;
|
||||
|
||||
query.valid = true;
|
||||
|
||||
if (query.qclass != 0xFF && query.qclass != 0x01) {
|
||||
// QCLASS is not ANY (0xFF) or INternet (0x01).
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.print(" **ERROR QCLASS** ");
|
||||
Serial.println(query.qclass, HEX);
|
||||
#endif
|
||||
query.valid = false;
|
||||
}
|
||||
|
||||
if (buffer_pointer >= data_size) {
|
||||
// We've over-run the returned data.
|
||||
// Something has gone wrong receiving or parsing the data.
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.print(" **ERROR size** ");
|
||||
Serial.print(buffer_pointer, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.println(data_size, HEX);
|
||||
#endif
|
||||
query.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MDns::Parse_Answer(Answer& answer) {
|
||||
#ifdef DEBUG_OUTPUT
|
||||
answer.buffer_pointer = buffer_pointer;
|
||||
#endif
|
||||
|
||||
buffer_pointer = nameFromDnsPointer(answer.name_buffer, 0, MAX_MDNS_NAME_LEN,
|
||||
data_buffer, buffer_pointer);
|
||||
|
||||
answer.rrtype = (data_buffer[buffer_pointer++] << 8);
|
||||
answer.rrtype += data_buffer[buffer_pointer++];
|
||||
|
||||
byte rrclass_0 = data_buffer[buffer_pointer++];
|
||||
byte rrclass_1 = data_buffer[buffer_pointer++];
|
||||
answer.rrset = (0b10000000 & rrclass_0);
|
||||
answer.rrclass = ((rrclass_0 & 0b01111111) << 8) + rrclass_1;
|
||||
|
||||
answer.rrttl = (data_buffer[buffer_pointer++] << 24);
|
||||
answer.rrttl += (data_buffer[buffer_pointer++] << 16);
|
||||
answer.rrttl += (data_buffer[buffer_pointer++] << 8);
|
||||
answer.rrttl += data_buffer[buffer_pointer++];
|
||||
|
||||
if (buffer_pointer > data_size) {
|
||||
// We've over-run the returned data.
|
||||
// Something has gone wrong receiving or parsing the data.
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.print(" **ERROR size** ");
|
||||
Serial.print(buffer_pointer, HEX);
|
||||
Serial.print(" ");
|
||||
Serial.println(data_size, HEX);
|
||||
#endif
|
||||
answer.valid = false;
|
||||
return;
|
||||
}
|
||||
PopulateAnswerResult(&answer);
|
||||
|
||||
answer.valid = true;
|
||||
}
|
||||
|
||||
// Display packet contents in HEX.
|
||||
void MDns::DisplayRawPacket() const {
|
||||
// display the packet contents in HEX
|
||||
Serial.println("Raw packet");
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i <= data_size; i += 16) {
|
||||
Serial.print("0x");
|
||||
PrintHex(i >> 8); PrintHex(i);
|
||||
Serial.print(" ");
|
||||
for (j = 0; j < 16; j++) {
|
||||
if (i + j >= data_size) {
|
||||
break;
|
||||
}
|
||||
if (data_buffer[i + j] > 31 and data_buffer[i + j] < 128) {
|
||||
Serial.print((char)data_buffer[i + j]);
|
||||
} else {
|
||||
Serial.print(".");
|
||||
}
|
||||
}
|
||||
Serial.print(" ");
|
||||
for (j = 0; j < 16; j++) {
|
||||
if (i + j >= data_size) {
|
||||
break;
|
||||
}
|
||||
PrintHex(data_buffer[i + j]);
|
||||
Serial.print(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MDns::PopulateAnswerResult(Answer* answer) {
|
||||
int rdlength = (data_buffer[buffer_pointer++] << 8);
|
||||
rdlength += data_buffer[buffer_pointer++];
|
||||
|
||||
switch (answer->rrtype) {
|
||||
case MDNS_TYPE_A: // Returns a 32-bit IPv4 address
|
||||
if (MAX_MDNS_NAME_LEN >= 16) {
|
||||
sprintf(answer->rdata_buffer, "%u.%u.%u.%u",
|
||||
data_buffer[buffer_pointer], data_buffer[buffer_pointer +1],
|
||||
data_buffer[buffer_pointer +2], data_buffer[buffer_pointer +3]);
|
||||
} else {
|
||||
sprintf(answer->rdata_buffer, "ipv4");
|
||||
}
|
||||
buffer_pointer += 4;
|
||||
break;
|
||||
case MDNS_TYPE_PTR: // Pointer to a canonical name.
|
||||
buffer_pointer = nameFromDnsPointer(answer->rdata_buffer, 0, MAX_MDNS_NAME_LEN,
|
||||
data_buffer, buffer_pointer);
|
||||
break;
|
||||
case MDNS_TYPE_HINFO: // HINFO. host information
|
||||
buffer_pointer = parseText(answer->rdata_buffer, MAX_MDNS_NAME_LEN, rdlength,
|
||||
data_buffer, buffer_pointer);
|
||||
break;
|
||||
case MDNS_TYPE_TXT: // Originally for arbitrary human-readable text in a DNS record.
|
||||
// We only return the first MAX_MDNS_NAME_LEN bytes of thir record type.
|
||||
buffer_pointer = parseText(answer->rdata_buffer, MAX_MDNS_NAME_LEN, rdlength,
|
||||
data_buffer, buffer_pointer);
|
||||
break;
|
||||
case MDNS_TYPE_AAAA: // Returns a 128-bit IPv6 address.
|
||||
{
|
||||
int buffer_pos = 0;
|
||||
for (int i = 0; i < rdlength; i++) {
|
||||
if (buffer_pos < MAX_MDNS_NAME_LEN - 3) {
|
||||
sprintf(answer->rdata_buffer + buffer_pos, "%02X:", data_buffer[buffer_pointer++]);
|
||||
} else {
|
||||
buffer_pointer++;
|
||||
}
|
||||
buffer_pos += 3;
|
||||
}
|
||||
answer->rdata_buffer[--buffer_pos] = '\0'; // Remove trailing ':'
|
||||
}
|
||||
break;
|
||||
case MDNS_TYPE_SRV: // Server Selection.
|
||||
{
|
||||
unsigned int priority = (data_buffer[buffer_pointer++] << 8);
|
||||
priority += data_buffer[buffer_pointer++];
|
||||
unsigned int weight = (data_buffer[buffer_pointer++] << 8);
|
||||
weight += data_buffer[buffer_pointer++];
|
||||
unsigned int port = (data_buffer[buffer_pointer++] << 8);
|
||||
port += data_buffer[buffer_pointer++];
|
||||
sprintf(answer->rdata_buffer, "p=%u;w=%u;port=%u;host=", priority, weight, port);
|
||||
|
||||
buffer_pointer = nameFromDnsPointer(answer->rdata_buffer, strlen(answer->rdata_buffer),
|
||||
MAX_MDNS_NAME_LEN - strlen(answer->rdata_buffer) -1, data_buffer, buffer_pointer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
int buffer_pos = 0;
|
||||
for (int i = 0; i < rdlength; i++) {
|
||||
if (buffer_pos < MAX_MDNS_NAME_LEN - 3) {
|
||||
sprintf(answer->rdata_buffer + buffer_pos, "%02X ", data_buffer[buffer_pointer++]);
|
||||
} else {
|
||||
buffer_pointer++;
|
||||
}
|
||||
buffer_pos += 3;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MDns::~MDns(){
|
||||
Udp.stop();
|
||||
};
|
||||
|
||||
bool writeToBuffer(const byte value, char* p_name_buffer, int* p_name_buffer_pos,
|
||||
const int name_buffer_len) {
|
||||
if (*p_name_buffer_pos < name_buffer_len - 1) {
|
||||
*(p_name_buffer + *p_name_buffer_pos) = value;
|
||||
(*p_name_buffer_pos)++;
|
||||
*(p_name_buffer + *p_name_buffer_pos) = '\0';
|
||||
return true;
|
||||
}
|
||||
(*p_name_buffer_pos)++;
|
||||
return false;
|
||||
}
|
||||
|
||||
int parseText(char* data_buffer, const int data_buffer_len, const int data_len,
|
||||
const byte* p_packet_buffer, int packet_buffer_pos) {
|
||||
int i, data_buffer_pos = 0;
|
||||
for (i = 0; i < data_len; i++) {
|
||||
writeToBuffer(p_packet_buffer[packet_buffer_pos++], data_buffer, &data_buffer_pos, data_buffer_len);
|
||||
}
|
||||
data_buffer[data_buffer_pos] = '\0';
|
||||
return packet_buffer_pos;
|
||||
}
|
||||
|
||||
int nameFromDnsPointer(char* p_name_buffer, int name_buffer_pos, const int name_buffer_len,
|
||||
const byte* p_packet_buffer, int packet_buffer_pos) {
|
||||
return nameFromDnsPointer(p_name_buffer, name_buffer_pos, name_buffer_len,
|
||||
p_packet_buffer, packet_buffer_pos, false);
|
||||
}
|
||||
|
||||
int nameFromDnsPointer(char* p_name_buffer, int name_buffer_pos, const int name_buffer_len,
|
||||
const byte* p_packet_buffer, int packet_buffer_pos, const bool recurse){
|
||||
if (recurse) {
|
||||
// Since we are adding more to an already populated buffer,
|
||||
// replace the trailing EOL with the FQDN seperator.
|
||||
name_buffer_pos--;
|
||||
writeToBuffer('.', p_name_buffer, &name_buffer_pos, name_buffer_len);
|
||||
}
|
||||
|
||||
if (p_packet_buffer[packet_buffer_pos] < 0xC0) {
|
||||
// Since the first 2 bits are not set,
|
||||
// this is the start of a name section.
|
||||
// http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique.htm
|
||||
|
||||
const int word_len = p_packet_buffer[packet_buffer_pos++];
|
||||
for (int l = 0; l < word_len; l++) {
|
||||
writeToBuffer(*(p_packet_buffer + packet_buffer_pos++), p_name_buffer,
|
||||
&name_buffer_pos, name_buffer_len);
|
||||
}
|
||||
|
||||
writeToBuffer('\0', p_name_buffer, &name_buffer_pos, name_buffer_len);
|
||||
|
||||
if (p_packet_buffer[packet_buffer_pos] > 0) {
|
||||
// Next word.
|
||||
packet_buffer_pos = nameFromDnsPointer(p_name_buffer, name_buffer_pos,
|
||||
name_buffer_len, p_packet_buffer,
|
||||
packet_buffer_pos, true);
|
||||
} else {
|
||||
// End of string.
|
||||
packet_buffer_pos++;
|
||||
}
|
||||
} else {
|
||||
// Message Compression used. Next 2 bytes are a pointer to the actual name section.
|
||||
int pointer = (p_packet_buffer[packet_buffer_pos++] - 0xC0) << 8;
|
||||
pointer += p_packet_buffer[packet_buffer_pos++];
|
||||
nameFromDnsPointer(p_name_buffer, name_buffer_pos, name_buffer_len,
|
||||
p_packet_buffer, pointer, false);
|
||||
}
|
||||
return packet_buffer_pos;
|
||||
}
|
||||
|
||||
void Query::Display() const {
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.print("question 0x");
|
||||
Serial.println(buffer_pointer, HEX);
|
||||
#endif
|
||||
if (!valid) {
|
||||
Serial.println(" **ERROR**");
|
||||
}
|
||||
Serial.print(" QNAME: ");
|
||||
Serial.println(qname_buffer);
|
||||
Serial.print(" QTYPE: 0x");
|
||||
Serial.print(qtype, HEX);
|
||||
Serial.print(" QCLASS: 0x");
|
||||
Serial.print(qclass, HEX);
|
||||
Serial.print(" Unicast Response: ");
|
||||
Serial.println(unicast_response);
|
||||
}
|
||||
|
||||
void Answer::Display() const {
|
||||
#ifdef DEBUG_OUTPUT
|
||||
Serial.print("answer 0x");
|
||||
Serial.println(buffer_pointer, HEX);
|
||||
#endif
|
||||
if (!valid) {
|
||||
Serial.println(" **ERROR**");
|
||||
}
|
||||
Serial.print(" RRNAME: ");
|
||||
Serial.println(name_buffer);
|
||||
Serial.print(" RRTYPE: 0x");
|
||||
Serial.print(rrtype, HEX);
|
||||
Serial.print(" RRCLASS: 0x");
|
||||
Serial.print(rrclass, HEX);
|
||||
Serial.print(" RRTTL: ");
|
||||
Serial.print(rrttl);
|
||||
Serial.print(" RRSET: ");
|
||||
Serial.println(rrset);
|
||||
Serial.print(" RRDATA: ");
|
||||
Serial.println(rdata_buffer);
|
||||
}
|
||||
|
||||
} // namespace mdns
|
||||
@@ -0,0 +1,244 @@
|
||||
#ifndef MDNS_H
|
||||
#define MDNS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#define DEBUG_STATISTICS // Record how many incoming packets fitted into data_buffer.
|
||||
//#define DEBUG_OUTPUT // Send packet summaries to Serial.
|
||||
//#define DEBUG_RAW // Send HEX ans ASCII encoded raw packet to Serial.
|
||||
|
||||
|
||||
#define MDNS_TYPE_A 0x0001
|
||||
#define MDNS_TYPE_PTR 0x000C
|
||||
#define MDNS_TYPE_HINFO 0x000D
|
||||
#define MDNS_TYPE_TXT 0x0010
|
||||
#define MDNS_TYPE_AAAA 0x001C
|
||||
#define MDNS_TYPE_SRV 0x0021
|
||||
|
||||
#define MDNS_TARGET_PORT 5353
|
||||
#define MDNS_SOURCE_PORT 5353
|
||||
#define MDNS_TTL 255
|
||||
|
||||
// Make this as big as memory limitations allow.
|
||||
// This default value can be overridden using the max_packet_size_ parameter of
|
||||
// MDns().
|
||||
#define MAX_PACKET_SIZE 1024
|
||||
|
||||
// The mDNS spec says this should never be more than 256 (including trailing '\0').
|
||||
#define MAX_MDNS_NAME_LEN 256
|
||||
|
||||
namespace mdns{
|
||||
|
||||
// A single mDNS Query.
|
||||
typedef struct Query{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
unsigned int buffer_pointer; // Position of Answer in packet. (Used for debugging only.)
|
||||
#endif
|
||||
char qname_buffer[MAX_MDNS_NAME_LEN]; // Question Name: Contains the object, domain or zone name.
|
||||
unsigned int qtype; // Question Type: Type of question being asked by client.
|
||||
unsigned int qclass; // Question Class: Normally the value 1 for Internet (“IN”)
|
||||
bool unicast_response; //
|
||||
bool valid; // False if problems were encountered decoding packet.
|
||||
|
||||
void Display() const; // Display a summary of this Answer on Serial port.
|
||||
} Query;
|
||||
|
||||
// A single mDNS Answer.
|
||||
typedef struct Answer{
|
||||
#ifdef DEBUG_OUTPUT
|
||||
unsigned int buffer_pointer; // Position of Answer in packet. (Used for debugging only.)
|
||||
#endif
|
||||
char name_buffer[MAX_MDNS_NAME_LEN]; // object, domain or zone name.
|
||||
char rdata_buffer[MAX_MDNS_NAME_LEN]; // The data portion of the resource record.
|
||||
unsigned int rrtype; // ResourceRecord Type.
|
||||
unsigned int rrclass; // ResourceRecord Class: Normally the value 1 for Internet (“IN”)
|
||||
unsigned long int rrttl; // ResourceRecord Time To Live: Number of seconds ths should be remembered.
|
||||
bool rrset; // Flush cache of records matching this name.
|
||||
bool valid; // False if problems were encountered decoding packet.
|
||||
|
||||
void Display() const ; // Display a summary of this Answer on Serial port.
|
||||
} Answer;
|
||||
|
||||
class MDns {
|
||||
private:
|
||||
public:
|
||||
// Simple constructor does not fire any callbacks on incoming data.
|
||||
// Default incoming data_buffer size is used.
|
||||
MDns() : MDns(NULL, NULL, NULL, MAX_PACKET_SIZE) {}
|
||||
|
||||
// Simple constructor does not fire any callbacks on incoming data.
|
||||
// Args:
|
||||
// max_packet_size_ : Set the data_buffer size allocated to store incoming packets.
|
||||
MDns(int max_packet_size_) : MDns(NULL, NULL, NULL, max_packet_size_) {}
|
||||
|
||||
// Constructor takes callbacks which fire when mDNS data arrives.
|
||||
// Args:
|
||||
// p_packet_function : Callback fires for every mDNS packet that arrives.
|
||||
// p_query_function : Callback fires for every mDNS Query that arrives as part of a packet.
|
||||
// p_answer_function : Callback fires for every mDNS Answer that arrives as part of a packet.
|
||||
MDns(std::function<void(const MDns*)> p_packet_function,
|
||||
std::function<void(const Query*)> p_query_function,
|
||||
std::function<void(const Answer*)> p_answer_function) :
|
||||
MDns(p_packet_function, p_query_function, p_answer_function, MAX_PACKET_SIZE) { }
|
||||
|
||||
// Constructor takes callbacks which fire when mDNS data arrives.
|
||||
// Args:
|
||||
// p_packet_function : Callback fires for every mDNS packet that arrives.
|
||||
// p_query_function : Callback fires for every mDNS Query that arrives as part of a packet.
|
||||
// p_answer_function : Callback fires for every mDNS Answer that arrives as part of a packet.
|
||||
// max_packet_size_ : Set the data_buffer size allocated to store incoming packets.
|
||||
MDns(std::function<void(const MDns*)> p_packet_function,
|
||||
std::function<void(const Query*)> p_query_function,
|
||||
std::function<void(const Answer*)> p_answer_function,
|
||||
int max_packet_size_) :
|
||||
#ifdef DEBUG_STATISTICS
|
||||
buffer_size_fail(0),
|
||||
largest_packet_seen(0),
|
||||
packet_count(0),
|
||||
#endif
|
||||
p_packet_function_(p_packet_function),
|
||||
p_query_function_(p_query_function),
|
||||
p_answer_function_(p_answer_function),
|
||||
buffer_pointer(0),
|
||||
data_buffer(new byte[max_packet_size_]),
|
||||
max_packet_size(max_packet_size_)
|
||||
{
|
||||
this->startUdpMulticast();
|
||||
};
|
||||
|
||||
// Constructor can be passed the buffer to hold the mDNS data.
|
||||
// This way the potentially large buffer can be shared with other processes.
|
||||
// Args:
|
||||
// p_packet_function : Callback fires for every mDNS packet that arrives.
|
||||
// p_query_function : Callback fires for every mDNS Query that arrives as part of a packet.
|
||||
// p_answer_function : Callback fires for every mDNS Answer that arrives as part of a packet.
|
||||
// max_packet_size_ : Set the data_buffer size allocated to store incoming packets.
|
||||
MDns(std::function<void(const MDns*)> p_packet_function,
|
||||
std::function<void(const Query*)> p_query_function,
|
||||
std::function<void(const Answer*)> p_answer_function,
|
||||
byte* data_buffer_,
|
||||
int max_packet_size_) :
|
||||
#ifdef DEBUG_STATISTICS
|
||||
buffer_size_fail(0),
|
||||
largest_packet_seen(0),
|
||||
packet_count(0),
|
||||
#endif
|
||||
p_packet_function_(p_packet_function),
|
||||
p_query_function_(p_query_function),
|
||||
p_answer_function_(p_answer_function),
|
||||
buffer_pointer(0),
|
||||
data_buffer(data_buffer_),
|
||||
max_packet_size(max_packet_size_)
|
||||
{
|
||||
this->startUdpMulticast();
|
||||
};
|
||||
|
||||
~MDns();
|
||||
|
||||
// Call this regularly to check for an incoming packet.
|
||||
bool loop();
|
||||
// Deprecated. Use loop() instead.
|
||||
bool Check(){
|
||||
return loop();
|
||||
}
|
||||
|
||||
// Send this MDns packet.
|
||||
void Send() const;
|
||||
|
||||
// Resets everything to represent an empty packet.
|
||||
// Do this before building a packet for sending.
|
||||
void Clear();
|
||||
|
||||
// Add a query to packet prior to sending.
|
||||
// May only be done before any Answers have been added.
|
||||
bool AddQuery(const Query& query);
|
||||
|
||||
// Add an answer to packet prior to sending.
|
||||
bool AddAnswer(const Answer& answer);
|
||||
|
||||
// Display a summary of the packet on Serial port.
|
||||
void Display() const;
|
||||
|
||||
// Display the raw packet in HEX and ASCII.
|
||||
void DisplayRawPacket() const;
|
||||
|
||||
#ifdef DEBUG_STATISTICS
|
||||
// Counter gets increased every time an incoming mDNS packet arrives that does
|
||||
// not fit in the data_buffer.
|
||||
unsigned int buffer_size_fail;
|
||||
|
||||
// Track the largest mDNS packet that has arrived.
|
||||
// Useful for knowing what size to make data_buffer.
|
||||
unsigned int largest_packet_seen;
|
||||
|
||||
// How many mDNS packets have arrived so far.
|
||||
unsigned int packet_count;
|
||||
#endif
|
||||
private:
|
||||
// Initializes udp multicast
|
||||
void startUdpMulticast();
|
||||
|
||||
void Parse_Query(Query& query);
|
||||
void Parse_Answer(Answer& answer);
|
||||
unsigned int PopulateName(const char* name_buffer);
|
||||
void PopulateAnswerResult(Answer* answer);
|
||||
|
||||
// Pointer to function that gets called for every incoming mDNS packet.
|
||||
std::function<void(const MDns*)> p_packet_function_;
|
||||
|
||||
// Pointer to function that gets called for every incoming query.
|
||||
std::function<void(const Query*)> p_query_function_;
|
||||
|
||||
// Pointer to function that gets called for every incoming answer.
|
||||
std::function<void(const Answer*)> p_answer_function_;
|
||||
|
||||
// Position in data_buffer while processing packet.
|
||||
unsigned int buffer_pointer;
|
||||
|
||||
// Buffer containing mDNS packet.
|
||||
byte* data_buffer;
|
||||
|
||||
// Buffer size for incoming MDns packet.
|
||||
unsigned int max_packet_size;
|
||||
|
||||
// Size of mDNS packet.
|
||||
unsigned int data_size;
|
||||
|
||||
// Query or Answer
|
||||
bool type;
|
||||
|
||||
// Whether more follows in another packet.
|
||||
bool truncated;
|
||||
|
||||
// Number of Qeries in the packet.
|
||||
unsigned int query_count;
|
||||
|
||||
// Number of Answers in the packet.
|
||||
unsigned int answer_count;
|
||||
|
||||
unsigned int ns_count;
|
||||
unsigned int ar_count;
|
||||
};
|
||||
|
||||
|
||||
// Display a byte on serial console in hexadecimal notation,
|
||||
// padding with leading zero if necisary to provide evenly tabulated display data.
|
||||
void PrintHex(unsigned char data);
|
||||
|
||||
// Extract Name from DNS data. Will follow pointers used by Message Compression.
|
||||
// TODO Check for exceeding packet size.
|
||||
int nameFromDnsPointer(char* p_name_buffer, int name_buffer_pos, const int name_buffer_len,
|
||||
const byte* p_packet_buffer, int packet_buffer_pos);
|
||||
int nameFromDnsPointer(char* p_name_buffer, int name_buffer_pos, const int name_buffer_len,
|
||||
const byte* p_packet_buffer, int packet_buffer_pos, const bool recurse);
|
||||
|
||||
bool writeToBuffer(const byte value, char* p_name_buffer, int* p_name_buffer_pos, const int name_buffer_len);
|
||||
|
||||
int parseText(char* data_buffer, const int data_buffer_len, int const data_len,
|
||||
const byte* p_packet_buffer, int packet_buffer_pos);
|
||||
|
||||
} // namespace mdns
|
||||
|
||||
#endif // MDNS_H
|
||||
Reference in new issue
Block a user