Arduino: Second mdns instance cannot be started

Created on 14 Apr 2020  Â·  32Comments  Â·  Source: esp8266/Arduino

Second mdns instance cannot be created becuase listening sockel listen on inaddr_any (0)
To be able to create second instance you'd probably need to replace
lib/ESP8266mDNS/src/LEAmDNS_Helpers.cpp

if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT))
to something like

if (m_pUDPContext->listen(ip_2_ip4(&m_netif->ip_addr), DNS_MQUERY_PORT))
To prevent err -8 ERR_USE on second instance

MDNS

All 32 comments

@BbIKTOP Thanks for the hint !
Did you modify your sources and checked if you can have both instances running with such fix ?

@d-a-v yes, sure, I changed this single line and now i have 2 instances running. Also i connected 2 computers, one to the esp ap and another one to the same wifi that esp sta connected to, and observe esp mdns via dns-sd and tcpdump on both computers

Hi @BbIKTOP there is a little side effect, probably only for me :) . I use a 2nd interface (an extra WiFI adapter) to get all OTA ports populated to Arduino IDE Win10. The LAN (current iface) has troubles (because I use WoL, I guess) so now the ESPs cannot find their way. Just FYI.

@lorol Sorry, could you explain further? I can't see how is it related - I mean, what is the influence to the OTA, when mDNS listens on it's interface only? And what exact troubles do you have?

@BbIKTOP it is my particular problem of having 2x network adapters (2x IPs) on same subnet - on the PC I run ArduinoIDE from. I guess, your implementation is fully correct it just doesn't work for me and I will hold on it. My problem is in PC setup somewhere that makes original LAN adapter inconsistently "seeing" the ESPs on my network. I just plugged an extra WiFi USB stick (no time to fix why the LAN adapter have issues) and all was OK until this change.
Anyway ... again this is another problem but before it worked now it doesn't. That's all.

You can disregard it, no problem. Hope no one else will have weird needs :)

Just from curiosity, why the original code several years was exposing IP4_ADDR_ANY - any reason (other than your testing convenience) to be not limited to &m_netif->ip_addr?

Didn’t get it but ok. I suppose, nobody tried to use it on both interfaces simultaneously, i mean on sta and ap. I. e. nobody tried to run 2 instances yet. Have no another versions

@BbIKTOP Have you noticed #7281 ?
Do you think you can try it with your two-interface environment ?
It still offers current LEAmDNS(v1) by default but you can enable the new version by commenting the right ligne in ESP8266mDNS.h.

Sorry, Dave, bad people want me to do my job. Again. I noticed that and definitely return to it this week. Will let you know, surely. Thank you!

@BbIKTOP Have you noticed #7281 ?
Do you think you can try it with your two-interface environment ?
It still offers current LEAmDNS(v1) by default but you can enable the new version by commenting the right ligne in ESP8266mDNS.h.

Sorry for delay, still have a lot to do (
Just tested it and it does not work. I observe it on both ifaces only if it has been [re]started after wifi sta got ip number from the network, which is almost ok. But also, it reports the same IP number it has got as a sta, to their clients connected toit as a soft ap, which is obviously wrong.

@BbIKTOP Have you noticed #7281 ?
Do you think you can try it with your two-interface environment ?
It still offers current LEAmDNS(v1) by default but you can enable the new version by commenting the right ligne in ESP8266mDNS.h.

Let me explain it a bit further (just in case)
I have wifi network with dhcp ip numbers range 172.16.5.0/24
ESP is configured as WIFI_AP_STA, softAP is configured as 10.1.1.0/24 with 10.1.1.1 assigned to softAP itself.
When connected to the WiFi and after it obtained an ip number, for example 172.16.5.99, it is accessible by it's mdns name ok.
Then, I connect a client to it, this client has an ip number 10.1.1.2 assigned by ESP. And this client still receive 172.16.5.99 as a ESP ip number, which is wrong because this network is not reachable
by the client and it shall get 10.1.1.1.
That's it.

By default this PR does not change anything.
There are two testing paths:

First one is to test v2 with Legacy-compatibility:

#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
// Maps the implementation to use to the global namespace type
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder;                // Legacy
//using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder;         // LEA
using MDNSResponder = esp8266::MDNSImplementation::clsLEAMDNSHost_Legacy;   // LEAv2Compat
//using MDNSResponder = clsMDNSHost;                                        // LEAv2

extern MDNSResponder MDNS;
#endif

Second one is the new API. That may or may not compile with your application.
If it doesn't, you may or may not update it for the test.

#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
// Maps the implementation to use to the global namespace type
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder;                // Legacy
//using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder;         // LEA
//using MDNSResponder = esp8266::MDNSImplementation::clsLEAMDNSHost_Legacy; // LEAv2Compat
using MDNSResponder = clsMDNSHost;                                          // LEAv2

extern MDNSResponder MDNS;
#endif

By default this PR does not change anything.
There are two testing paths:

First one is to test v2 with Legacy-compatibility:

#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
// Maps the implementation to use to the global namespace type
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder;                // Legacy
//using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder;         // LEA
using MDNSResponder = esp8266::MDNSImplementation::clsLEAMDNSHost_Legacy;   // LEAv2Compat
//using MDNSResponder = clsMDNSHost;                                        // LEAv2

extern MDNSResponder MDNS;
#endif

Second one is the new API. That may or may not compile with your application.
If it doesn't, you may or may not update it for the test.

#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
// Maps the implementation to use to the global namespace type
//using MDNSResponder = Legacy_MDNSResponder::MDNSResponder;                // Legacy
//using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder;         // LEA
//using MDNSResponder = esp8266::MDNSImplementation::clsLEAMDNSHost_Legacy; // LEAv2Compat
using MDNSResponder = clsMDNSHost;                                          // LEAv2

extern MDNSResponder MDNS;
#endif

Please help me understand - do I need to uncomment that? What shall I change in order to test? I have not defined either NO_GLOBAL_INSTANCES nor NO_GLOBAL_MDNS. I wrongly supposed there's some #define that switch new functionality on and off
Shall I uncomment LEAv2 for the new one and LEA for the new compatible one?

By default LEA is uncommented.
LEAv2Legacy is the new one compatible with current one.
You can comment all of them except LEAv2Legacy for a start.

Tried leav2compat - it is working as supposed, thank you!

Tried leav2 - cannot get it working since there’s no ::registerService(char,char,unsigned short), just ::registerService(char,char,char*,unsigned short)
Tried both registerService(0,...) and registerService(“someHostname”,...) but cannot see it via bonjour on both softap and wifi networks in any case

@BbIKTOP LEAv2Compat is using LEAv2 and has the same API as LEAv1.

Tried leav2compat - it is working as supposed, thank you!

Thanks for the report !

I am so lost... so it used to be one instance that works on all interfaces. Then it was restricted, and need to have one instance for each interface. Now the plan is to bring back one instance for all interfaces? In my opinion it should be one instance for all interfaces. It should work smoothly at least for AP_STA. Not sure how it would work for multiple IP interfaces on STA mode. But that seems to be the rare case.

I am so lost... so it used to be one instance that works on all interfaces. Then it was restricted, and need to have one instance for each interface. Now the plan is to bring back one instance for all interfaces? In my opinion it should be one instance for all interfaces. It should work smoothly at least for AP_STA. Not sure how it would work for multiple IP interfaces on STA mode. But that seems to be the rare case.
It’s a rare case for simple kindergarten apps, school apps and so on use ap_sta mode and 2 ip numbers.

Actually, single instance never worked. I asked for assistance and I’ve been told to use 2 instances. It sounded strange to use different instances on different ifaces, i never heard about such an implementation, also it’s even more strange to have 2 instances on such a resources poor device, but ok, who am i to object.
I tried to run second instance and found then, that running multiple instances is not possible due to inaddr_any binding of listening socket (it is usually not possible to bind multiple listening sockets to the same ip number:port) I started to study sources, they are... let say, a bit overcomplicated, so my first wish was to write my own implementation, bonjour is quite simple protocol, to be honest. But then I found the place in code and thought that I have to complete my project first. So, i changed sources to make each instance bound to it’s interface only. Tested it and it was working.
Then people started to complain they have problems with it. And then project owners decided to “repair” original version to make single instance working on all interfaces, as I asked at the very beginning and how it’s done in all other implementations I know (only 2 really, lol)

@ramiws,

Like @BbIKTOP is telling, a single instance was not properly working under all circumstances with both interface at the same time. There have been several attempts to fix this and the original author @LaborEtArs ended up with providing an updated version in #7281 (not enabled by default) that works with an instance per interface (at least confirmed to be working by @BbIKTOP and me). We may merge it for the soon-to-be-released version 2.7.2 of this core.

I am currently working on stripping this new version to make it working with a single instance for all interfaces (even ethernet interfaces) but it will not be ready for 2.7.2.

The updated MDNS code is still showing issues and requires more work. Per internal discussions, pushing back to v3.

In git version, a single mDNS instance will now work on all interfaces. Can you try it ?

Sorry, was a bit busy working. Please remind me, is it enough to add lib_deps=https://github.com/esp8266/Arduino in pio to test?

Tried it, and looks like it's working. Have esp8266 12e on my custom board, running in WiFi.mode(WIFI_AP_STA); mode, connected to the wifi. Successfully pinging it by .local name from another computer, connected to the same wifi/net. Also, have connected a phone to the esp's wifi sta, and able to ping it by the same name simultaneously.

Didn't I forget to test anything?

I believe than you need to use the "stage" configuration in PIO (if that points to the current git master branch).

Simply added lib_deps=https://github.com/esp8266/Arduino, because I supposed that it adds current master to the project. To be honest, I am not too familiar with pio. Since it is working, may I assume it uses master and everything is working fine?

Didn't I forget to test anything?
may I assume it uses master and everything is working fine?

If you are able to see the advertisement from both networks, then I think this is fine !

Edit: You'll close this issue when you are conviced it is now OK :)

Using this code, and ESP hostname 'box':

#include <Arduino.h>

#include <ESP8266mDNS.h>
#include <ESP8266WiFi.h>

#define dPrintf(...)            \
  {                             \
    Serial.printf(__VA_ARGS__); \
    delay(500);                 \
  }

extern char *wifiNet;
extern char *wifiPassword;

char *apName = (char *)"box";
char *apPassword = (char *)"box-12345";
char *apIp = (char *)"10.1.1.1";
char *apMask = (char *)"255.255.255.0";

// MDNSResponder mdnsInt, mdnsExt;

void setup()
{
  int res;

  Serial.begin(115200);
  delay(1000);
  Serial.println("\nStarting mDNS test");

  WiFi.persistent(false);
  WiFi.disconnect();

  WiFi.mode(WIFI_AP_STA);

  IPAddress localIp;
  IPAddress localMask;
  IPAddress localGw;

  localIp.fromString(apIp);
  localMask.fromString(apMask);
  localGw.fromString(apIp);

  res = WiFi.softAP(apName, apPassword); //, 2, false, 20);
  Serial.printf("After softAP: [%s] res=%d, [%s]\n", apName, res, WiFi.localIP().toString().c_str());

  res = WiFi.softAPConfig(localIp, localGw, localMask);
  Serial.printf("After softAPConfig: %d\n", res);

  Serial.printf("After softAP IP: %s\n", WiFi.softAPIP().toString().c_str());

  WiFi.hostname(apName);
  WiFi.setAutoReconnect(true);
  WiFi.begin();

  Serial.printf("Local ip is %s\n", WiFi.softAPIP().toString().c_str());

  if (!MDNS.begin(apName))
  {
    Serial.printf("Cannot start internal mDNS responder\n");
  }
  else
  {
    Serial.printf("Internal mDNS responder started\n");
  }
  MDNS.addService("http", "tcp", 80);
  //MDNS.addService(0, "http", "tcp", 80);

  Serial.println("Started");
}

#define WAIT_COUNT 20
void loop()
{
  static int counter = 0;
  static int counter2 = 0;

  static int lastStatus = WiFi.status();

  dPrintf("Updating MDNS\n");
  MDNS.update();

  Serial.printf("Wifi status=%d, previous status=%d, ip=%s, local ip=%s\n",
                (int)WiFi.status(), lastStatus,
                WiFi.localIP().toString().c_str(),
                WiFi.softAPIP().toString().c_str());

  if (counter++ > WAIT_COUNT)
  {
    if (WiFi.status() == WL_IDLE_STATUS)
    {
      Serial.println("Reconnecting...");
      WiFi.begin(wifiNet, wifiPassword);
      // MDNS.end();
      // MDNS.begin(apName);
      //WiFi.reconnect();
    }

    if (WiFi.status() == WL_CONNECTED && lastStatus != WL_CONNECTED)
    {
      Serial.printf("!!!!!!!!!! Connected, IP is: %s\n", WiFi.localIP().toString().c_str());
      counter = 0;

      //MDNS.end();
      //MDNS.begin(apName);
    }
    lastStatus = WiFi.status();
  }
  if (lastStatus == WL_CONNECTED && WiFi.status() != WL_CONNECTED)
  {
    lastStatus = WiFi.status();
    // MDNS.end();
    // MDNS.begin(apName);
  }

  if (counter2++ > WAIT_COUNT)
  {
    dPrintf("Querying services for int\n");
    // int scInt = MDNS.queryService("http", "tcp");
    //MDNS.notifyAPChange();
  }

  delay(1000);
}

I observe:

1) From the computer on the same Wi-Fi/IP network, to which ESP is connected as a client:

viktor@Work:~ $ dns-sd -B
Browsing for _http._tcp
DATE: ---Fri 16 Oct 2020---
11:15:33.183  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
11:15:33.183  Add        3   5 local.               _http._tcp.          HIKVISION DS-2CD2363G0-I - C73529589
11:15:33.183  Add        3   5 local.               _http._tcp.          HIKVISION DS-2CD2363G0-I - C73529699
11:15:33.183  Add        2   5 local.               _http._tcp.          PRO-10S
11:16:24.877  Add        2   5 local.               _http._tcp.          box
^C
viktor@Work:~ $ ping box.local
PING box.local (172.16.5.249): 56 data bytes
64 bytes from 172.16.5.249: icmp_seq=0 ttl=255 time=88.192 ms
64 bytes from 172.16.5.249: icmp_seq=1 ttl=255 time=2.854 ms
64 bytes from 172.16.5.249: icmp_seq=2 ttl=255 time=2.167 ms
^C
--- box.local ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 2.167/31.071/88.192/40.392 ms

2) Phone has been successfully connected to the ESP's Wi-Fi AP, I was able to ping ESP by its name (box.local in this case), and ping utility shows me correct IP from the 10/8 network:
IMG_4966

Please confirm I didn't miss anything, before I close it. Thank you so much!

I think nothing is missed !
That's the kind of test we were doing with @hreintke.

Well, I'm closing the issue then. Thank you!

Just to summarize, the correct setup right now is to call MDNS.begin() only once? re. commented out blocks in the example above. I have only tested this briefly, but can also confirm that everything works as expected.

OT, on the same commented out blocks. Needs a new issue I suppose
Looking at the new ::begin() though, I have to assume it would install multiple lwipcb's after calling it more than once? It also never removes those callbacks as it seems, so we end up doing ::_restart() even after ::close() / ::end() was called, allocating UDP all over again?

These commented out blocks left from the previous testing, but I didn't try ::begin() ::end() in loop() yet

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tttapa picture tttapa  Â·  3Comments

hulkco picture hulkco  Â·  3Comments

gosewski picture gosewski  Â·  3Comments

mechanic98 picture mechanic98  Â·  3Comments

treii28 picture treii28  Â·  3Comments