Fixed mdns bind issue on windows

This commit is contained in:
robert 2025-05-06 10:06:46 -04:00
parent 041ec6e1f8
commit 4e2f7beeeb
4 changed files with 55 additions and 32 deletions

View File

@ -461,8 +461,13 @@ static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) {
void mg_multicast_add(struct mg_connection *c, char *ip);
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) {
#if MG_ARCH == MG_ARCH_WIN32
const char *mcast_url = "udp://0.0.0.0:5353";
#else
const char *mcast_url = "udp://224.0.0.251:5353";
#endif
struct mg_connection *c =
mg_listen(mgr, "udp://224.0.0.251:5353", mdns_cb, name);
mg_listen(mgr, mcast_url, mdns_cb, name);
if (c != NULL) mg_multicast_add(c, (char *)"224.0.0.251");
return c;
}
@ -8418,27 +8423,33 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET;
int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
int sockopt = -1;
(void) on;
#if defined(SO_EXCLUSIVEADDRUSE)
// Using SO_REUSEADDR for UDP sockets on Windows (issue #3125)
sockopt = proto == IPPROTO_UDP ? SO_REUSEADDR : SO_EXCLUSIVEADDRUSE;
// "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
sockopt = SO_REUSEADDR;
// 1. SO_REUSEADDR semantics on UNIX and Windows is different. On
// Windows, SO_REUSEADDR allows to bind a socket to a port without error
// even if the port is already open by another program. This is not the
// behavior SO_REUSEADDR was designed for, and leads to hard-to-track
// failure scenarios.
//
// 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
// SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
// won't work! (setsockopt will return EINVAL)
#endif
if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) {
MG_ERROR(("socket: %d", MG_SOCK_ERR(-1)));
#if defined(SO_EXCLUSIVEADDRUSE)
} else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
} else if ((sockopt != -1) && (rc = setsockopt(fd, SOL_SOCKET, sockopt,
(char *) &on, sizeof(on))) != 0) {
// "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
#if defined(SO_EXCLUSIVEADDRUSE)
MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc)));
#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
} else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
sizeof(on))) != 0) {
// 1. SO_REUSEADDR semantics on UNIX and Windows is different. On
// Windows, SO_REUSEADDR allows to bind a socket to a port without error
// even if the port is already open by another program. This is not the
// behavior SO_REUSEADDR was designed for, and leads to hard-to-track
// failure scenarios.
//
// 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
// SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
// won't work! (setsockopt will return EINVAL)
#else
MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc)));
#endif
#if MG_IPV6_V6ONLY

View File

@ -340,8 +340,13 @@ static void mdns_cb(struct mg_connection *c, int ev, void *ev_data) {
void mg_multicast_add(struct mg_connection *c, char *ip);
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, char *name) {
#if MG_ARCH == MG_ARCH_WIN32
const char *mcast_url = "udp://0.0.0.0:5353";
#else
const char *mcast_url = "udp://224.0.0.251:5353";
#endif
struct mg_connection *c =
mg_listen(mgr, "udp://224.0.0.251:5353", mdns_cb, name);
mg_listen(mgr, mcast_url, mdns_cb, name);
if (c != NULL) mg_multicast_add(c, (char *)"224.0.0.251");
return c;
}

View File

@ -207,27 +207,33 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET;
int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
int sockopt = -1;
(void) on;
#if defined(SO_EXCLUSIVEADDRUSE)
// Using SO_REUSEADDR for UDP sockets on Windows (issue #3125)
sockopt = proto == IPPROTO_UDP ? SO_REUSEADDR : SO_EXCLUSIVEADDRUSE;
// "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
sockopt = SO_REUSEADDR;
// 1. SO_REUSEADDR semantics on UNIX and Windows is different. On
// Windows, SO_REUSEADDR allows to bind a socket to a port without error
// even if the port is already open by another program. This is not the
// behavior SO_REUSEADDR was designed for, and leads to hard-to-track
// failure scenarios.
//
// 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
// SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
// won't work! (setsockopt will return EINVAL)
#endif
if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) {
MG_ERROR(("socket: %d", MG_SOCK_ERR(-1)));
#if defined(SO_EXCLUSIVEADDRUSE)
} else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
} else if ((sockopt != -1) && (rc = setsockopt(fd, SOL_SOCKET, sockopt,
(char *) &on, sizeof(on))) != 0) {
// "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
#if defined(SO_EXCLUSIVEADDRUSE)
MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc)));
#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE)
} else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
sizeof(on))) != 0) {
// 1. SO_REUSEADDR semantics on UNIX and Windows is different. On
// Windows, SO_REUSEADDR allows to bind a socket to a port without error
// even if the port is already open by another program. This is not the
// behavior SO_REUSEADDR was designed for, and leads to hard-to-track
// failure scenarios.
//
// 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining
// SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but
// won't work! (setsockopt will return EINVAL)
#else
MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc)));
#endif
#if MG_IPV6_V6ONLY

View File

@ -12,6 +12,7 @@ int main(void) {
// Desired name must NOT have any dots in it, nor a domain
c = mg_mdns_listen(&mgr, "Mongoose"); // Start mDNS server
if (!c) return 1;
// if not using our built-in TCP/IP stack, pass the IP address you want to
// use as a response, this depends on your underlying TCP/IP stack and number
// of interfaces available