Re: dbus-daemon on Windows: Failed to bind socket "localhost:59367": Address already in use
Thomas Sondergaard <ts-2vlBz+Gd9o2sRyR5/[email protected]>
| Newsgroups | gmane.comp.freedesktop.dbus |
|---|---|
| Message-ID | <[email protected]> |
Hi Ralf, On 2015-01-03 10:24, Ralf Habacker wrote: > > Am 03.01.2015 um 01:43 schrieb Thomas Sondergaard: >> On 2015-01-02 23:17, Thomas Sondergaard wrote: >>> When I run the attached test programs I get a handful or so of cases >>> where the dbus-daemon fails to start with a message like >>> >>> Failed to bind socket "localhost:59367": Address already in use >>> >>> This surprises me. First I thought it was some kind of race condition >>> when starting multiple buses at the same time (dbus-test.cc), but it >>> also fails a handful of times out of a thousand when only one process is >>> started at a time (dbus-test-nothreads.cc). >>> >>> This is using D-Bus Message Bus Daemon 1.8.10 on Windows. >>> >>> Steps to reproduce: >>> >>> 1. Compile the small test program on Windows >>> 2. Make sure dbus-daemon is in the path >>> 3. Run the test program >>> >>> The nothreads version takes about 4 minutes to run. >>> >>> Regards, >>> Thomas >> >> I've digged a little further and I think I see a bug in >> _dbus_server_new_for_tcp_socket() in dbus-sysdeps-win.c. If I >> understand _dbus_server_new_for_tcp_socket() it is working hard to >> bind the same port on both IPv4 and IPv6. If it starts with port=0 it >> will do a bind with that and then extract the port number with >> getsockname(), and then it will goto redo_lookup_with_port and do the >> same binds again. >> >> The bug that I'm seeing is that it is not correctly extracting the >> port number that the first bind with port=0 produces. It is doing: >> >> snprintf( portbuf, sizeof( portbuf ) - 1, "%d", addr.AddressIn.sin_port); >> >> which fails to account for sin_port being in network byte order. The >> effect is that subsequent binds will be to another "arbitrary" port >> that may or may not be available. >> >> Just adding ntohs() around addr.AddressIn.sin_port will make the >> function find the correct port, but it wont work as the next bind() >> will then fail with EADDRINUSE. >> >> I compared the Windows and the Unix version of >> _dbus_server_new_for_tcp_socket() and it seems that the two versions >> of the functions have grown apart over time. Perhaps the unix version >> has some tricks that the Windows version needs to learn? > The difference in _dbus_listen_tcp_socket is that the unix version uses > getnameinfo() to fetch the port, which is implemented in the appended > patch for windows too. Can you check if this works ? > > Regards > Ralf > It doesn't work. The second call to bind after the port number chosen by the kernel has been obtained fails. I have stolen another fragment from the unix version of the function and it now works correctly. See attached patch. Regards, Thomas
0001-Bind-correctly-to-ephemeral-port.patch
(text/x-patch, 3 KB)
From 022ed59e5bbda3bdc73b0fdc845bc907a3e9090e Mon Sep 17 00:00:00 2001 From: Thomas Sondergaard <ts-2vlBz+Gd9o2sRyR5/[email protected]> Date: Sat, 3 Jan 2015 14:10:33 +0100 Subject: [PATCH] Bind correctly to ephemeral port Fix binding to wrong port due to network vs host byte order issue. Bring Windows version of _dbus_server_new_for_tcp_socket() more in sync with unix version. --- dbus/dbus-sysdeps-win.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 66c397f..0939c10 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -1469,10 +1469,18 @@ _dbus_listen_tcp_socket (const char *host, if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR) { DBUS_SOCKET_SET_ERRNO (); + closesocket(fd); + if (errno == WSAEADDRINUSE) + { + /* Depending on kernel policy, it may or may not + be neccessary to bind to both IPv4 & 6 addresses + so ignore EADDRINUSE here */ + tmp = tmp->ai_next; + continue; + } dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to bind socket \"%s:%s\": %s", host ? host : "*", port, _dbus_strerror_from_errno ()); - closesocket (fd); goto failed; } @@ -1506,19 +1514,24 @@ _dbus_listen_tcp_socket (const char *host, to use the same port */ if (!port || !strcmp(port, "0")) { - mysockaddr_gen addr; - socklen_t addrlen = sizeof(addr); - char portbuf[10]; - - if (getsockname(fd, &addr.Address, &addrlen) == SOCKET_ERROR) + int result; + struct sockaddr_storage addr; + socklen_t addrlen; + char portbuf[50]; + + addrlen = sizeof(addr); + result = getsockname(fd, (struct sockaddr*) &addr, &addrlen); + + if (result == -1 || + (res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0, + portbuf, sizeof(portbuf), + NI_NUMERICHOST)) != 0) { - DBUS_SOCKET_SET_ERRNO (); dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to resolve port \"%s:%s\": %s", - host ? host : "*", port, _dbus_strerror_from_errno()); + "Failed to resolve port \"%s:%s\": %s (%s)", + host ? host : "*", port, gai_strerror(res), res); goto failed; } - snprintf( portbuf, sizeof( portbuf ) - 1, "%d", addr.AddressIn.sin_port ); if (!_dbus_string_append(retport, portbuf)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);