From 8b00358901ce55055e896e50672fcd877eb218db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 23 May 2022 14:20:41 +0200 Subject: [PATCH] [core] Add lwIP-based mDNS responder library --- arduino/libretuya/libraries/mDNS/ESPmDNS.h | 5 + arduino/libretuya/libraries/mDNS/LwIPmDNS.cpp | 151 ++++++++++++++++++ arduino/libretuya/libraries/mDNS/mDNS.h | 119 ++++++++++++++ builder/frameworks/realtek-ambz-arduino.py | 2 + builder/libs/lwip.py | 1 + docs/config.md | 1 + platform/realtek-ambz/fixups/inc/lwipopts.h | 9 +- 7 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 arduino/libretuya/libraries/mDNS/ESPmDNS.h create mode 100644 arduino/libretuya/libraries/mDNS/LwIPmDNS.cpp create mode 100644 arduino/libretuya/libraries/mDNS/mDNS.h diff --git a/arduino/libretuya/libraries/mDNS/ESPmDNS.h b/arduino/libretuya/libraries/mDNS/ESPmDNS.h new file mode 100644 index 0000000..8580167 --- /dev/null +++ b/arduino/libretuya/libraries/mDNS/ESPmDNS.h @@ -0,0 +1,5 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-05-23. */ + +#pragma once + +#include "mDNS.h" diff --git a/arduino/libretuya/libraries/mDNS/LwIPmDNS.cpp b/arduino/libretuya/libraries/mDNS/LwIPmDNS.cpp new file mode 100644 index 0000000..9b9604e --- /dev/null +++ b/arduino/libretuya/libraries/mDNS/LwIPmDNS.cpp @@ -0,0 +1,151 @@ +/* Copyright (c) Kuba SzczodrzyƄski 2022-05-23. */ + +#ifdef LT_HAS_LWIP2 + +#include "mDNS.h" + +extern "C" { +#include +#include +} + +extern u8_t mdns_netif_client_id; + +struct mdns_domain { + /* Encoded domain name */ + u8_t name[256]; + /* Total length of domain name, including zero */ + u16_t length; + /* Set if compression of this domain is not allowed */ + u8_t skip_compression; +}; + +/** Description of a service */ +struct mdns_service { + /** TXT record to answer with */ + struct mdns_domain txtdata; + /** Name of service, like 'myweb' */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Type of service, like '_http' */ + char service[MDNS_LABEL_MAXLEN + 1]; + /** Callback function and userdata + * to update txtdata buffer */ + service_get_txt_fn_t txt_fn; + void *txt_userdata; + /** TTL in seconds of SRV/TXT replies */ + u32_t dns_ttl; + /** Protocol, TCP or UDP */ + u16_t proto; + /** Port of the service */ + u16_t port; +}; + +/** Description of a host/netif */ +struct mdns_host { + /** Hostname */ + char name[MDNS_LABEL_MAXLEN + 1]; + /** Pointer to services */ + struct mdns_service *services[MDNS_MAX_SERVICES]; + /** TTL in seconds of A/AAAA/PTR replies */ + u32_t dns_ttl; +}; + +static String mdnsInstanceName = "default_instance"; + +mDNS::mDNS() {} + +mDNS::~mDNS() {} + +bool mDNS::begin(const char *hostname) { + mdns_resp_init(); + struct netif *netif = netif_list; + while (netif != NULL) { + if (netif_is_up(netif) && mdns_resp_add_netif(netif, hostname, 255) != ERR_OK) { + return false; + } + netif = netif->next; + } + return true; +} + +void mDNS::end() { + struct netif *netif = netif_list; + while (netif != NULL) { + if (netif_is_up(netif)) + mdns_resp_remove_netif(netif); + netif = netif->next; + } +} + +void mDNS::setInstanceName(String name) { + mdnsInstanceName = name; +} + +bool mDNS::addService(char *service, char *proto, uint16_t port) { + char _service[strlen(service) + 2]; + char _proto[strlen(proto) + 2]; + _service[0] = '_'; + _proto[0] = '_'; + // prepend names with _ + strcpy(_service + 1, service + (service[0] == '_')); + strcpy(_proto + 1, proto + (proto[0] == '_')); + + mdns_sd_proto protocol = DNSSD_PROTO_UDP; + if (strncmp(_proto + 1, "tcp", 3) == 0) + protocol = DNSSD_PROTO_TCP; + + struct netif *netif = netif_list; + while (netif != NULL) { + if (netif_is_up(netif)) { + mdns_resp_add_service(netif, mdnsInstanceName.c_str(), service, protocol, port, 255, NULL, NULL); + } + netif = netif->next; + } +} + +bool mDNS::addServiceTxt(char *name, char *proto, char *key, char *value) { + char _name[strlen(name) + 2]; + char _proto[strlen(proto) + 2]; + _name[0] = '_'; + _proto[0] = '_'; + // prepend names with _ + strcpy(_name + 1, name + (name[0] == '_')); + strcpy(_proto + 1, proto + (proto[0] == '_')); + + mdns_sd_proto protocol = DNSSD_PROTO_UDP; + if (strncmp(_proto + 1, "tcp", 3) == 0) + protocol = DNSSD_PROTO_TCP; + + struct netif *netif = netif_list; + struct mdns_host *mdns; + struct mdns_service *service; + + uint8_t txt_len = strlen(key) + strlen(value) + 1; + char *txt = (char *)malloc(txt_len + 1); + sprintf(txt, "%s=%s", key, value); + + while (netif != NULL) { + if (netif_is_up(netif)) { + mdns = (struct mdns_host *)netif_get_client_data(netif, mdns_netif_client_id); + + for (uint8_t i = 0; i < MDNS_MAX_SERVICES; i++) { + service = mdns->services[i]; + if (service == NULL) + continue; + if (strcmp(service->service, _name) || service->proto != protocol) + continue; + if (mdns_resp_add_service_txtitem(service, txt, txt_len) != ERR_OK) { + free(txt); + return false; + } + } + } + netif = netif->next; + } + free(txt); + return true; +} + +MDNSResponder MDNS; + +#endif diff --git a/arduino/libretuya/libraries/mDNS/mDNS.h b/arduino/libretuya/libraries/mDNS/mDNS.h new file mode 100644 index 0000000..225b446 --- /dev/null +++ b/arduino/libretuya/libraries/mDNS/mDNS.h @@ -0,0 +1,119 @@ +/* +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 (ivan@esp8266.com) +MDNS-SD Suport 2015 Hristo Gochkov (hristo@espressif.com) +Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) +Rewritten for ESP32 by Hristo Gochkov (hristo@espressif.com) + +This is a simple implementation of multicast DNS query support for an Arduino +running on ESP32 chip. + +Usage: +- Include the ESP32 Multicast DNS library in the sketch. +- 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. + +*/ + +#pragma once + +#include +#include + +class mDNS { + public: + mDNS(); + ~mDNS(); + + bool begin(const char *hostname); + void end(); + + void setInstanceName(String name); + bool addService(char *service, char *proto, uint16_t port); + bool addServiceTxt(char *name, char *proto, char *key, char *value); + // void enableArduino(uint16_t port = 3232, bool auth = false); + // void disableArduino(); + // void enableWorkstation(esp_interface_t interface = ESP_IF_WIFI_STA); + // void disableWorkstation(); + + IPAddress queryHost(char *host, uint32_t timeout = 2000); + int queryService(char *service, char *proto); + + String hostname(int idx); + IPAddress IP(int idx); + IPv6Address IPv6(int idx); + uint16_t port(int idx); + int numTxt(int idx); + bool hasTxt(int idx, const char *key); + String txt(int idx, const char *key); + String txt(int idx, int txtIdx); + String txtKey(int idx, int txtIdx); + + void setInstanceName(const char *name) { + setInstanceName(String(name)); + } + + void setInstanceName(char *name) { + setInstanceName(String(name)); + } + + bool addService(const char *service, const char *proto, uint16_t port) { + return addService((char *)service, (char *)proto, port); + } + + bool addService(String service, String proto, uint16_t port) { + return addService(service.c_str(), proto.c_str(), port); + } + + void addServiceTxt(const char *name, const char *proto, const char *key, const char *value) { + addServiceTxt((char *)name, (char *)proto, (char *)key, (char *)value); + } + + void addServiceTxt(String name, String proto, String key, String value) { + addServiceTxt(name.c_str(), proto.c_str(), key.c_str(), value.c_str()); + } + + IPAddress queryHost(const char *host, uint32_t timeout = 2000) { + return queryHost((char *)host, timeout); + } + + IPAddress queryHost(String host, uint32_t timeout = 2000) { + return queryHost(host.c_str(), timeout); + } + + int queryService(const char *service, const char *proto) { + return queryService((char *)service, (char *)proto); + } + + int queryService(String service, String proto) { + return queryService(service.c_str(), proto.c_str()); + } +}; + +typedef mDNS MDNSResponder; + +extern MDNSResponder MDNS; diff --git a/builder/frameworks/realtek-ambz-arduino.py b/builder/frameworks/realtek-ambz-arduino.py index dcbed40..ff6d7bb 100644 --- a/builder/frameworks/realtek-ambz-arduino.py +++ b/builder/frameworks/realtek-ambz-arduino.py @@ -52,6 +52,8 @@ env.Append( ("bool", "bool"), # enable LwIPRxBuffer ("LT_HAS_LWIP", "1"), + # enable LwIPmDNS + ("LT_HAS_LWIP2", "1"), ("LT_PRINTF_BROKEN", "1"), # printf does not handle %.3f properly ], LINKFLAGS=[ diff --git a/builder/libs/lwip.py b/builder/libs/lwip.py index 29a40ee..a113731 100644 --- a/builder/libs/lwip.py +++ b/builder/libs/lwip.py @@ -35,6 +35,7 @@ def env_add_lwip( "+", "+", # 2.0.x "+", # 1.4.x + "+", *port_srcs, ], includes=[ diff --git a/docs/config.md b/docs/config.md index 0a34675..ae10315 100644 --- a/docs/config.md +++ b/docs/config.md @@ -40,3 +40,4 @@ Platforms should generally call i.e. WiFiClient debugging for client-related cod ## Platform options - LT_HAS_LWIP - whether platform SDK has LwIP. This causes `LwIPRxBuffer.cpp` to be compiled for platform libraries to use. +- LT_HAS_LWIP2 - whether platform has LwIP v2.0.0 or newer. This causes `LwIPmDNS.cpp` to be compiled. diff --git a/platform/realtek-ambz/fixups/inc/lwipopts.h b/platform/realtek-ambz/fixups/inc/lwipopts.h index aae0b09..1ffec4f 100644 --- a/platform/realtek-ambz/fixups/inc/lwipopts.h +++ b/platform/realtek-ambz/fixups/inc/lwipopts.h @@ -1,5 +1,8 @@ // CHANGES: // - 2022-05-08 undefine LWIP_PROVIDE_ERRNO +// - 2022-05-23 enable LWIP_MDNS_RESPONDER +// - 2022-05-23 set LWIP_NUM_NETIF_CLIENT_DATA to 1 +// - 2022-05-23 set MEMP_NUM_UDP_PCB to 7 /** @@ -83,7 +86,7 @@ a lot of data that needs to be copied, this should be set high. */ #define MEMP_NUM_PBUF 100 /* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One per active UDP "connection". */ -#define MEMP_NUM_UDP_PCB 6 +#define MEMP_NUM_UDP_PCB 7 /* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. */ #define MEMP_NUM_TCP_PCB 10 @@ -319,6 +322,10 @@ The STM32F2x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums #define DNS_IGNORE_REPLY_ERR 1 #endif /* DNS_IGNORE_REPLY_ERR */ +// for mDNS support +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_NUM_NETIF_CLIENT_DATA 1 + #endif /* __LWIPOPTS_H__ */ /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/