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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.