Switch over to using getaddrinfo for TCP clients & servers to enable IPv6
authorDaniel P. Berrange <dan@berrange.com>
Wed, 25 Jul 2007 02:46:52 +0000 (02:46 +0000)
committerDaniel P. Berrange <dan@berrange.com>
Wed, 25 Jul 2007 02:46:52 +0000 (02:46 +0000)
ChangeLog
bus/dbus-daemon.1.in
dbus/dbus-address.c
dbus/dbus-server-socket.c
dbus/dbus-server-socket.h
dbus/dbus-server-unix.c
dbus/dbus-sysdeps-unix.c
dbus/dbus-sysdeps.h
dbus/dbus-transport-socket.c
dbus/dbus-transport-socket.h

index 3f7918e..ce7ca33 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2007-07-24  Daniel P. Berrange <dan@berrange.com>
+
+       * bus/dbus-daemon.1: Add docs on new syntax options for the bus
+       address strings
+
+       * dbus/dbus-address.c: Allow * in addresses (for binding to all
+       addresses).
+
+       * dbus/dbus-sysdeps.h:
+       * dbus/dbus-sysdeps-unix.c: Re-write to use getaddrinfo instead
+       of gethostbyname to enable protocol independant name lookup,
+       making IPv6 work
+
+       * dbus/dbus-server-socket.h:
+       * dbus/dbus-server-socket.c: Add support for 'family' in the
+       address string to specify ipv4 vs ipv6. Use a port string to
+       allow for service resolution. Allow for binding to multiple
+       sockets at once in case of dual IPv4 & IPv6 stacks.
+
+       * dbus/dbus-server-unix.c: Pass in an array of file descriptors
+       instead of a single one.
+
+       * dbus/dbus-transport-socket.h:
+       * dbus/dbus-transport-socket.c: Add support for 'family' in the
+       address string to specify ipv4 vs ipv6. Use a port string to
+       allow for service resolution.
+
 2007-07-24  Havoc Pennington  <hp@redhat.com>
 
        * configure.in: add AM_PROG_CC_C_O to allow per-target CPPFLAGS
index d4d816f..63d9718 100644 (file)
@@ -221,6 +221,15 @@ the last address given in <listen> first. That is,
 apps will try to connect to the last <listen> address first.
 
 .PP
+tcp sockets can accept IPv4 addresses, IPv6 addresses or hostnames.
+If a hostname resolves to multiple addresses, the server will bind
+to all of them. The family=ipv4 or family=ipv6 options can be used
+to force it to bind to a subset of addresses
+
+.PP
+Example: <listen>tcp:host=localhost,port=0,family=ipv4</listen>
+
+.PP
 A special case is using a port number of zero (or omitting the port),
 which means to choose an available port selected by the operating
 system. The port number chosen can be with the --print-address command
@@ -231,11 +240,15 @@ reports its own address, such as when DBUS_SESSION_BUS_ADDRESS is set.
 Example: <listen>tcp:host=localhost,port=0</listen>
 
 .PP
-tcp addresses also allow an all_interfaces=true option, which will
-cause the bus to listen on all local address (INADDR_ANY) and not only
-the specified host. However, the specified host will still be used as
-the reported address of the server. The specified host should be a
-valid name of the local machine or weird stuff will happen.
+tcp addresses also allow a bind=hostname option, which will override
+the host option specifying what address to bind to, without changing
+the address reported by the bus. The bind option can also take a
+special name '*' to cause the bus to listen on all local address
+(INADDR_ANY). The specified host should be a valid name of the local
+machine or weird stuff will happen.
+
+.PP
+Example: <listen>tcp:host=localhost,bind=*,port=0</listen>
 
 .TP
 .I "<auth>"
index 97f8882..0b87b8e 100644 (file)
@@ -89,6 +89,7 @@ _dbus_set_bad_address (DBusError  *error,
           (b) == '_' ||                                 \
           (b) == '/' ||                                 \
           (b) == '\\' ||                                \
+          (b) == '*' ||                                \
           (b) == '.')
 
 /**
@@ -691,7 +692,6 @@ static const char* invalid_escaped_values[] = {
   "%",
   "$",
   " ",
-  "*"
 };
 
 dbus_bool_t
index c414bd1..c8ae38a 100644 (file)
@@ -47,8 +47,9 @@ typedef struct DBusServerSocket DBusServerSocket;
 struct DBusServerSocket
 {
   DBusServer base;   /**< Parent class members. */
-  int fd;            /**< File descriptor or -1 if disconnected. */
-  DBusWatch *watch;  /**< File descriptor watch. */
+  int n_fds;         /**< Number of active file handles */
+  int *fds;          /**< File descriptor or -1 if disconnected. */
+  DBusWatch **watch; /**< File descriptor watch. */
   char *socket_name; /**< Name of domain socket, to unlink if appropriate */
 };
 
@@ -56,15 +57,19 @@ static void
 socket_finalize (DBusServer *server)
 {
   DBusServerSocket *socket_server = (DBusServerSocket*) server;
+  int i;
   
   _dbus_server_finalize_base (server);
 
-  if (socket_server->watch)
-    {
-      _dbus_watch_unref (socket_server->watch);
-      socket_server->watch = NULL;
-    }
+  for (i = 0 ; i < socket_server->n_fds ; i++)
+    if (socket_server->watch[i])
+      {
+        _dbus_watch_unref (socket_server->watch[i]);
+        socket_server->watch[i] = NULL;
+      }
   
+  dbus_free (socket_server->fds);
+  dbus_free (socket_server->watch);
   dbus_free (socket_server->socket_name);
   dbus_free (server);
 }
@@ -149,10 +154,21 @@ socket_handle_watch (DBusWatch    *watch,
 {
   DBusServer *server = data;
   DBusServerSocket *socket_server = data;
+#ifndef DBUS_DISABLE_ASSERT
+  int i;
+  dbus_bool_t found = FALSE;
+#endif
 
   SERVER_LOCK (server);
   
-  _dbus_assert (watch == socket_server->watch);
+#ifndef DBUS_DISABLE_ASSERT
+  for (i = 0 ; i < socket_server->n_fds ; i++)
+    {
+      if (socket_server->watch[i] == watch)
+        found = TRUE;
+    }
+  _dbus_assert (found);
+#endif
 
   _dbus_verbose ("Handling client connection, flags 0x%x\n", flags);
   
@@ -199,19 +215,23 @@ static void
 socket_disconnect (DBusServer *server)
 {
   DBusServerSocket *socket_server = (DBusServerSocket*) server;
+  int i;
 
   HAVE_LOCK_CHECK (server);
   
-  if (socket_server->watch)
+  for (i = 0 ; i < socket_server->n_fds ; i++)
     {
-      _dbus_server_remove_watch (server,
-                                 socket_server->watch);
-      _dbus_watch_unref (socket_server->watch);
-      socket_server->watch = NULL;
+      if (socket_server->watch[i])
+        {
+          _dbus_server_remove_watch (server,
+                                     socket_server->watch[i]);
+          _dbus_watch_unref (socket_server->watch[i]);
+          socket_server->watch[i] = NULL;
+        }
+
+      _dbus_close_socket (socket_server->fds[i], NULL);
+      socket_server->fds[i] = -1;
     }
-  
-  _dbus_close_socket (socket_server->fd, NULL);
-  socket_server->fd = -1;
 
   if (socket_server->socket_name != NULL)
     {
@@ -236,89 +256,128 @@ static const DBusServerVTable socket_vtable = {
  * been successfully invoked on it. The server will use accept() to
  * accept new client connections.
  *
- * @param fd the file descriptor.
+ * @param fds list of file descriptors.
+ * @param n_fds number of file descriptors
  * @param address the server's address
  * @returns the new server, or #NULL if no memory.
  * 
  */
 DBusServer*
-_dbus_server_new_for_socket (int               fd,
+_dbus_server_new_for_socket (int              *fds,
+                             int               n_fds,
                              const DBusString *address)
 {
   DBusServerSocket *socket_server;
   DBusServer *server;
-  DBusWatch *watch;
+  int i;
   
   socket_server = dbus_new0 (DBusServerSocket, 1);
   if (socket_server == NULL)
     return NULL;
-  
-  watch = _dbus_watch_new (fd,
-                           DBUS_WATCH_READABLE,
-                           TRUE,
-                           socket_handle_watch, socket_server,
-                           NULL);
-  if (watch == NULL)
+
+  socket_server->fds = dbus_new (int, n_fds);
+  if (!socket_server->fds)
+    goto failed_0;
+
+  socket_server->watch = dbus_new0 (DBusWatch *, n_fds);
+  if (!socket_server->watch)
+    goto failed_1;
+
+  for (i = 0 ; i < n_fds ; i++)
     {
-      dbus_free (socket_server);
-      return NULL;
+      DBusWatch *watch;
+
+      watch = _dbus_watch_new (fds[i],
+                               DBUS_WATCH_READABLE,
+                               TRUE,
+                               socket_handle_watch, socket_server,
+                               NULL);
+      if (watch == NULL)
+        goto failed_2;
+
+      socket_server->n_fds++;
+      socket_server->fds[i] = fds[i];
+      socket_server->watch[i] = watch;
     }
-  
+
   if (!_dbus_server_init_base (&socket_server->base,
                                &socket_vtable, address))
-    {
-      _dbus_watch_unref (watch);
-      dbus_free (socket_server);
-      return NULL;
-    }
+    goto failed_2;
 
-  server = (DBusServer*) socket_server;
+  server = (DBusServer*)socket_server;
 
   SERVER_LOCK (server);
   
-  if (!_dbus_server_add_watch (&socket_server->base,
-                               watch))
+  for (i = 0 ; i < n_fds ; i++)
     {
-      SERVER_UNLOCK (server);
-      _dbus_server_finalize_base (&socket_server->base);
-      _dbus_watch_unref (watch);
-      dbus_free (socket_server);
-      return NULL;
+      if (!_dbus_server_add_watch (&socket_server->base,
+                                   socket_server->watch[i]))
+        {
+          int j;
+          for (j = 0 ; j < i ; j++)
+            _dbus_server_remove_watch (server,
+                                       socket_server->watch[j]);
+
+          SERVER_UNLOCK (server);
+          _dbus_server_finalize_base (&socket_server->base);
+          goto failed_2;
+        }
     }
-  
-  socket_server->fd = fd;
-  socket_server->watch = watch;
 
   SERVER_UNLOCK (server);
   
   return (DBusServer*) socket_server;
+
+ failed_2:
+  for (i = 0 ; i < n_fds ; i++)
+    {
+      if (socket_server->watch[i] != NULL)
+        {
+          _dbus_watch_unref (socket_server->watch[i]);
+          socket_server->watch[i] = NULL;
+        }
+    }
+  dbus_free (socket_server->watch);
+
+ failed_1:
+  dbus_free (socket_server->fds);
+
+ failed_0:
+  dbus_free (socket_server);
+  return NULL;
 }
 
 /**
  * Creates a new server listening on TCP.
- * If inaddr_any is TRUE, listens on all local interfaces.
- * Otherwise, it resolves the hostname and listens only on
- * the resolved address of the hostname. The hostname is used
- * even if inaddr_any is TRUE, as the hostname to report when
- * dbus_server_get_address() is called. If the hostname is #NULL,
- * localhost is used.
+ * If host is NULL, it will default to localhost.
+ * If bind is NULL, it will default to the value for the host
+ * parameter, and if that is NULL, then localhost
+ * If bind is a hostname, it will be resolved and will listen
+ * on all returned addresses.
+ * If family is NULL, hostname resolution will try all address
+ * families, otherwise it can be ipv4 or ipv6 to restrict the
+ * addresses considered.
  *
- * @param host the hostname to listen on.
+ * @param host the hostname to report for the listen address
+ * @param bind the hostname to listen on
  * @param port the port to listen on or 0 to let the OS choose
- * @param inaddr_any #TRUE to listen on all local interfaces
+ * @param family 
  * @param error location to store reason for failure.
  * @returns the new server, or #NULL on failure.
  */
 DBusServer*
 _dbus_server_new_for_tcp_socket (const char     *host,
-                                 dbus_uint32_t   port,
-                                 dbus_bool_t     inaddr_any,
+                                 const char     *bind,
+                                 const char     *port,
+                                 const char     *family,
                                  DBusError      *error)
 {
   DBusServer *server;
-  int listen_fd;
+  int *listen_fds = NULL;
+  int nlisten_fds = 0, i;
   DBusString address;
   DBusString host_str;
+  DBusString port_str;
   
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
 
@@ -328,44 +387,77 @@ _dbus_server_new_for_tcp_socket (const char     *host,
       return NULL;
     }
 
+  if (!_dbus_string_init (&port_str))
+    {
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_0;
+    }
+
   if (host == NULL)
     host = "localhost";
-  
-  listen_fd = _dbus_listen_tcp_socket (host, &port, inaddr_any, error);
-  _dbus_fd_set_close_on_exec (listen_fd);
+
+  if (port == NULL)
+    port = "0";
+
+  if (bind == NULL)
+    bind = host;
+  else if (strcmp (bind, "*") == 0)
+    bind = NULL;
+
+  nlisten_fds =_dbus_listen_tcp_socket (bind, port, family,
+                                        &port_str,
+                                        &listen_fds, error);
+  if (nlisten_fds <= 0)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET(error);
+      goto failed_1;
+    }
+
+  for (i = 0 ; i < nlisten_fds ; i++)
+    _dbus_fd_set_close_on_exec (listen_fds[i]);
 
   _dbus_string_init_const (&host_str, host);
   if (!_dbus_string_append (&address, "tcp:host=") ||
       !_dbus_address_append_escaped (&address, &host_str) ||
       !_dbus_string_append (&address, ",port=") ||
-      !_dbus_string_append_int (&address, port))
+      !_dbus_string_append (&address, _dbus_string_get_const_data(&port_str)))
     {
-      _dbus_string_free (&address);
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
-      return NULL;
+      goto failed_2;
     }
-
-  
-  if (listen_fd < 0)
+  if (family &&
+      (!_dbus_string_append (&address, ",family=") ||
+       !_dbus_string_append (&address, family)))
     {
-      _dbus_string_free (&address);
-      return NULL;
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      goto failed_2;
     }
   
-  server = _dbus_server_new_for_socket (listen_fd, &address);
+  server = _dbus_server_new_for_socket (listen_fds, nlisten_fds, &address);
   if (server == NULL)
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
-      _dbus_close_socket (listen_fd, NULL);
-      _dbus_string_free (&address);
-      return NULL;
+      goto failed_2;
     }
 
+  _dbus_string_free (&port_str);
   _dbus_string_free (&address);
-  
+  dbus_free(listen_fds);
+
   return server;
 
+ failed_2:
+  for (i = 0 ; i < nlisten_fds ; i++)
+    _dbus_close_socket (listen_fds[i], NULL);
+  dbus_free(listen_fds);
+
+ failed_1:
+  _dbus_string_free (&port_str);
+
+ failed_0:
+  _dbus_string_free (&address);
 
+  return NULL;
 }
 
 /**
@@ -395,55 +487,16 @@ _dbus_server_listen_socket (DBusAddressEntry *entry,
     {
       const char *host;
       const char *port;
-      const char *all_interfaces;
-      dbus_bool_t inaddr_any;
-      long lport;
+      const char *bind;
+      const char *family;
 
       host = dbus_address_entry_get_value (entry, "host");
+      bind = dbus_address_entry_get_value (entry, "bind");
       port = dbus_address_entry_get_value (entry, "port");
-      all_interfaces = dbus_address_entry_get_value (entry, "all_interfaces");
+      family = dbus_address_entry_get_value (entry, "family");
 
-      inaddr_any = FALSE;
-      if (all_interfaces != NULL)
-        {
-          if (strcmp (all_interfaces, "true") == 0)
-            {
-              inaddr_any = TRUE;
-            }
-          else if (strcmp (all_interfaces, "false") == 0)
-            {
-              inaddr_any = FALSE;
-            }
-          else
-            {
-              _dbus_set_bad_address(error, NULL, NULL, 
-                                    "all_interfaces flag in tcp: address should be 'true' or 'false'");
-              return DBUS_SERVER_LISTEN_BAD_ADDRESS;
-            }
-        }
-      
-      if (port == NULL)
-        {
-          lport = 0;
-        }
-      else
-        {
-          dbus_bool_t sresult;
-          DBusString  str;
-          
-          _dbus_string_init_const (&str, port);
-          sresult = _dbus_string_parse_int (&str, 0, &lport, NULL);
-          _dbus_string_free (&str);
-          
-          if (sresult == FALSE || lport < 0 || lport > 65535)
-            {
-              _dbus_set_bad_address(error, NULL, NULL, 
-                                    "Port is not an integer between 0 and 65535");
-              return DBUS_SERVER_LISTEN_BAD_ADDRESS;
-            }
-        }
-          
-      *server_p = _dbus_server_new_for_tcp_socket (host, lport, inaddr_any, error);
+      *server_p = _dbus_server_new_for_tcp_socket (host, bind, port,
+                                                   family, error);
 
       if (*server_p)
         {
index 2023220..34900b4 100644 (file)
 
 DBUS_BEGIN_DECLS
 
-DBusServer* _dbus_server_new_for_socket           (int               fd,
+DBusServer* _dbus_server_new_for_socket           (int              *fds,
+                                                   int               n_fds,
                                                    const DBusString *address);
 DBusServer* _dbus_server_new_for_tcp_socket       (const char       *host,
-                                                   dbus_uint32_t     port,
-                                                   dbus_bool_t       inaddr_any,
+                                                   const char       *bind,
+                                                   const char       *port,
+                                                   const char       *family,
                                                    DBusError        *error);
 DBusServerListenResult _dbus_server_listen_socket (DBusAddressEntry  *entry,
                                                    DBusServer       **server_p,
index d765bee..1dda5d1 100644 (file)
@@ -209,7 +209,7 @@ _dbus_server_new_for_domain_socket (const char     *path,
       goto failed_1;
     }
   
-  server = _dbus_server_new_for_socket (listen_fd, &address);
+  server = _dbus_server_new_for_socket (&listen_fd, 1, &address);
   if (server == NULL)
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
index 2ea83df..0ab5e73 100644 (file)
@@ -737,65 +737,94 @@ _dbus_listen_unix_socket (const char     *path,
  * nonblocking.
  *
  * @param host the host name to connect to
- * @param port the prot to connect to
+ * @param port the port to connect to
+ * @param family the address family to listen on, NULL for all
  * @param error return location for error code
  * @returns connection file descriptor or -1 on error
  */
 int
 _dbus_connect_tcp_socket (const char     *host,
-                          dbus_uint32_t   port,
+                          const char     *port,
+                          const char     *family,
                           DBusError      *error)
 {
-  int fd;
-  struct sockaddr_in addr;
-  struct hostent *he;
-  struct in_addr *haddr;
+  int fd = -1, res;
+  struct addrinfo hints;
+  struct addrinfo *ai, *tmp;
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-  
+
   if (!_dbus_open_tcp_socket (&fd, error))
     {
       _DBUS_ASSERT_ERROR_IS_SET(error);
-      
       return -1;
     }
+
   _DBUS_ASSERT_ERROR_IS_CLEAR(error);
-      
-  if (host == NULL)
-    host = "localhost";
 
-  he = gethostbyname (host);
-  if (he == NULL) 
+  _DBUS_ZERO (hints);
+
+  if (!family)
+    hints.ai_family = AF_UNSPEC;
+  else if (!strcmp(family, "ipv4"))
+    hints.ai_family = AF_INET;
+  else if (!strcmp(family, "ipv6"))
+    hints.ai_family = AF_INET6;
+  else
     {
       dbus_set_error (error,
                       _dbus_error_from_errno (errno),
-                      "Failed to lookup hostname: %s",
-                      host);
-      _dbus_close (fd, NULL);
+                      "Unknown address family %s", family);
       return -1;
     }
-  
-  haddr = ((struct in_addr *) (he->h_addr_list)[0]);
+  fprintf(stderr, "Family %s\n", family ? family : "none");
+  hints.ai_protocol = IPPROTO_TCP;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_ADDRCONFIG;
 
-  _DBUS_ZERO (addr);
-  memcpy (&addr.sin_addr, haddr, sizeof(struct in_addr));
-  addr.sin_family = AF_INET;
-  addr.sin_port = htons (port);
-  
-  if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
-    {      
+  if ((res = getaddrinfo(host, port, &hints, &ai)) != 0)
+    {
       dbus_set_error (error,
-                       _dbus_error_from_errno (errno),
-                      "Failed to connect to socket %s:%d %s",
-                      host, port, _dbus_strerror (errno));
-
+                      _dbus_error_from_errno (errno),
+                      "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+                      host, port, gai_strerror(res), res);
       _dbus_close (fd, NULL);
-      fd = -1;
-      
       return -1;
     }
 
+  tmp = ai;
+  while (tmp)
+    {
+      if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
+        {
+          freeaddrinfo(ai);
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          return -1;
+        }
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
+
+      if (connect (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
+        {
+          _dbus_close(fd, NULL);
+          fd = -1;
+          tmp = tmp->ai_next;
+          continue;
+        }
+
+      break;
+    }
+  freeaddrinfo(ai);
+
+  if (fd == -1)
+    {
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to connect to socket \"%s:%s\" %s",
+                      host, port, _dbus_strerror(errno));
+      return -1;
+    }
+
+
   if (!_dbus_set_fd_nonblocking (fd, error))
     {
       _dbus_close (fd, NULL);
@@ -814,88 +843,186 @@ _dbus_connect_tcp_socket (const char     *host,
  * If inaddr_any is specified, the hostname is ignored.
  *
  * @param host the host name to listen on
- * @param port the prot to listen on, if zero a free port will be used
- * @param inaddr_any TRUE to listen on all local interfaces instead of on the host name
+ * @param port the port to listen on, if zero a free port will be used
+ * @param family the address family to listen on, NULL for all
+ * @param retport string to return the actual port listened on
+ * @param fds_p location to store returned file descriptors
  * @param error return location for errors
- * @returns the listening file descriptor or -1 on error
+ * @returns the number of listening file descriptors or -1 on error
  */
 int
 _dbus_listen_tcp_socket (const char     *host,
-                         dbus_uint32_t  *port,
-                         dbus_bool_t     inaddr_any,
+                         const char     *port,
+                         const char     *family,
+                         DBusString     *retport,
+                         int           **fds_p,
                          DBusError      *error)
 {
-  int listen_fd;
-  struct sockaddr_in addr;
-  socklen_t len = (socklen_t) sizeof (struct sockaddr);
+  int nlisten_fd = 0, *listen_fd = NULL, res, i;
+  struct addrinfo hints;
+  struct addrinfo *ai, *tmp;
 
-  _DBUS_ASSERT_ERROR_IS_CLEAR (error);  
-  
-  if (!_dbus_open_tcp_socket (&listen_fd, error))
+  *fds_p = NULL;
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _DBUS_ZERO (hints);
+
+  if (!family)
+    hints.ai_family = AF_UNSPEC;
+  else if (!strcmp(family, "ipv4"))
+    hints.ai_family = AF_INET;
+  else if (!strcmp(family, "ipv6"))
+    hints.ai_family = AF_INET6;
+  else
     {
-      _DBUS_ASSERT_ERROR_IS_SET(error);
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Unknown address family %s", family);
       return -1;
     }
-  _DBUS_ASSERT_ERROR_IS_CLEAR(error);
 
-  _DBUS_ZERO (addr);
-  
-  if (inaddr_any)
+  hints.ai_protocol = IPPROTO_TCP;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+
+ redo_lookup_with_port:
+  if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai)
     {
-      addr.sin_addr.s_addr = INADDR_ANY;
+      dbus_set_error (error,
+                      _dbus_error_from_errno (errno),
+                      "Failed to lookup host/port: \"%s:%s\": %s (%d)",
+                      host ? host : "*", port, gai_strerror(res), res);
+      return -1;
     }
-  else
+
+  tmp = ai;
+  while (tmp)
     {
-      struct hostent *he;
-      struct in_addr *haddr;      
+      int fd = -1, *newlisten_fd;
+      if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
+        {
+          _DBUS_ASSERT_ERROR_IS_SET(error);
+          goto failed;
+        }
+      _DBUS_ASSERT_ERROR_IS_CLEAR(error);
 
-      he = gethostbyname (host);
-      if (he == NULL) 
+      if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
         {
-          dbus_set_error (error,
-                          _dbus_error_from_errno (errno),
-                          "Failed to lookup hostname: %s",
-                          host);
-          _dbus_close (listen_fd, NULL);
-          return -1;
+          _dbus_close(fd, NULL);
+          if (errno == EADDRINUSE)
+            {
+              /* 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 (errno));
+          goto failed;
         }
-  
-      haddr = ((struct in_addr *) (he->h_addr_list)[0]);
-      
-      memcpy (&addr.sin_addr, haddr, sizeof (struct in_addr));
+
+      if (listen (fd, 30 /* backlog */) < 0)
+        {
+          _dbus_close (fd, NULL);
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to listen on socket \"%s:%s\": %s",
+                          host ? host : "*", port, _dbus_strerror (errno));
+          goto failed;
+        }
+
+      newlisten_fd = dbus_realloc(listen_fd, sizeof(int)*(nlisten_fd+1));
+      if (!newlisten_fd)
+        {
+          _dbus_close (fd, NULL);
+          dbus_set_error (error, _dbus_error_from_errno (errno),
+                          "Failed to allocate file handle array: %s",
+                          _dbus_strerror (errno));
+          goto failed;
+        }
+      listen_fd = newlisten_fd;
+      listen_fd[nlisten_fd] = fd;
+      nlisten_fd++;
+
+      if (!_dbus_string_get_length(retport))
+        {
+          /* If the user didn't specify a port, or used 0, then
+             the kernel chooses a port. After the first address
+             is bound to, we need to force all remaining addresses
+             to use the same port */
+          if (!port || !strcmp(port, "0"))
+            {
+              struct sockaddr_storage addr;
+              socklen_t addrlen;
+              char portbuf[50];
+
+              addrlen = sizeof(addr);
+              getsockname(fd, (struct sockaddr*) &addr, &addrlen);
+
+              if ((res = getnameinfo((struct sockaddr*)&addr, addrlen, NULL, 0,
+                                     portbuf, sizeof(portbuf),
+                                     NI_NUMERICHOST)) != 0)
+                {
+                  dbus_set_error (error, _dbus_error_from_errno (errno),
+                                  "Failed to resolve port \"%s:%s\": %s (%s)",
+                                  host ? host : "*", port, gai_strerror(res), res);
+                  goto failed;
+                }
+              if (!_dbus_string_append(retport, portbuf))
+                {
+                  dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                  goto failed;
+                }
+
+              /* Release current address list & redo lookup */
+              port = _dbus_string_get_const_data(retport);
+              freeaddrinfo(ai);
+              goto redo_lookup_with_port;
+            }
+          else
+            {
+              if (!_dbus_string_append(retport, port))
+                {
+                    dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+                    goto failed;
+                }
+            }
+        }
+
+      tmp = tmp->ai_next;
     }
-  
-  addr.sin_family = AF_INET;
-  addr.sin_port = htons (*port);
+  freeaddrinfo(ai);
+  ai = NULL;
 
-  if (bind (listen_fd, (struct sockaddr*) &addr, sizeof (struct sockaddr)))
+  if (!nlisten_fd)
     {
+      errno = EADDRINUSE;
       dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to bind socket \"%s:%d\": %s",
-                      host, *port, _dbus_strerror (errno));
-      _dbus_close (listen_fd, NULL);
+                      "Failed to bind socket \"%s:%s\": %s",
+                      host ? host : "*", port, _dbus_strerror (errno));
       return -1;
     }
 
-  if (listen (listen_fd, 30 /* backlog */) < 0)
+  for (i = 0 ; i < nlisten_fd ; i++)
     {
-      dbus_set_error (error, _dbus_error_from_errno (errno),  
-                      "Failed to listen on socket \"%s:%d\": %s",
-                      host, *port, _dbus_strerror (errno));
-      _dbus_close (listen_fd, NULL);
-      return -1;
+      if (!_dbus_set_fd_nonblocking (listen_fd[i], error))
+        {
+          goto failed;
+        }
     }
 
-  getsockname(listen_fd, (struct sockaddr*) &addr, &len);
-  *port = (dbus_uint32_t) ntohs(addr.sin_port);
+  *fds_p = listen_fd;
 
-  if (!_dbus_set_fd_nonblocking (listen_fd, error))
-    {
-      _dbus_close (listen_fd, NULL);
-      return -1;
-    }
-  
-  return listen_fd;
+  return nlisten_fd;
+
+ failed:
+  if (ai)
+    freeaddrinfo(ai);
+  for (i = 0 ; i < nlisten_fd ; i++)
+    _dbus_close(listen_fd[i], NULL);
+  dbus_free(listen_fd);
+  return -1;
 }
 
 static dbus_bool_t
@@ -1089,7 +1216,7 @@ _dbus_read_credentials_socket  (int              client_fd,
 #ifdef SO_PEERCRED
     struct ucred cr;   
     int cr_len = sizeof (cr);
-   
+    
     if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
        cr_len == sizeof (cr))
       {
index ab0119a..ab1631b 100644 (file)
@@ -144,11 +144,14 @@ int         _dbus_write_socket_two (int               fd,
                                     int               start2,
                                     int               len2);
 int _dbus_connect_tcp_socket  (const char     *host,
-                               dbus_uint32_t   port,
+                               const char     *port,
+                               const char     *family,
                                DBusError      *error);
 int _dbus_listen_tcp_socket   (const char     *host,
-                               dbus_uint32_t  *port,
-                               dbus_bool_t     inaddr_any,
+                               const char     *port,
+                               const char     *family,
+                               DBusString     *retport,
+                               int           **fds_p,
                                DBusError      *error);
 int _dbus_accept              (int             listen_fd);
 
index 8ec69d8..a31c159 100644 (file)
@@ -1204,15 +1204,18 @@ _dbus_transport_new_for_socket (int               fd,
 
 /**
  * Creates a new transport for the given hostname and port.
+ * If host is NULL, it will default to localhost
  *
  * @param host the host to connect to
  * @param port the port to connect to
+ * @param family the address family to connect to
  * @param error location to store reason for failure.
  * @returns a new transport, or #NULL on failure.
  */
 DBusTransport*
 _dbus_transport_new_for_tcp_socket (const char     *host,
-                                    dbus_int32_t    port,
+                                    const char     *port,
+                                    const char     *family,
                                     DBusError      *error)
 {
   int fd;
@@ -1226,20 +1229,27 @@ _dbus_transport_new_for_tcp_socket (const char     *host,
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       return NULL;
     }
-  
+
+  if (host == NULL)
+    host = "localhost";
+
   if (!_dbus_string_append (&address, "tcp:"))
     goto error;
 
-  if (host != NULL && 
-       (!_dbus_string_append (&address, "host=") ||
-        !_dbus_string_append (&address, host)))
+  if (!_dbus_string_append (&address, "host=") ||
+      !_dbus_string_append (&address, host))
     goto error;
 
   if (!_dbus_string_append (&address, ",port=") ||
-      !_dbus_string_append_int (&address, port))
+      !_dbus_string_append (&address, port))
     goto error;
 
-  fd = _dbus_connect_tcp_socket (host, port, error);
+  if (family != NULL &&
+      (!_dbus_string_append (&address, "family=") ||
+       !_dbus_string_append (&address, family)))
+    goto error;
+
+  fd = _dbus_connect_tcp_socket (host, port, family, error);
   if (fd < 0)
     {
       _DBUS_ASSERT_ERROR_IS_SET (error);
@@ -1249,7 +1259,7 @@ _dbus_transport_new_for_tcp_socket (const char     *host,
 
   _dbus_fd_set_close_on_exec (fd);
   
-  _dbus_verbose ("Successfully connected to tcp socket %s:%d\n",
+  _dbus_verbose ("Successfully connected to tcp socket %s:%s\n",
                  host, port);
   
   transport = _dbus_transport_new_for_socket (fd, NULL, &address);
@@ -1293,28 +1303,15 @@ _dbus_transport_open_socket(DBusAddressEntry  *entry,
     {
       const char *host = dbus_address_entry_get_value (entry, "host");
       const char *port = dbus_address_entry_get_value (entry, "port");
-      DBusString  str;
-      long lport;
-      dbus_bool_t sresult;
-          
+      const char *family = dbus_address_entry_get_value (entry, "family");
+
       if (port == NULL)
         {
           _dbus_set_bad_address (error, "tcp", "port", NULL);
           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
         }
 
-      _dbus_string_init_const (&str, port);
-      sresult = _dbus_string_parse_int (&str, 0, &lport, NULL);
-      _dbus_string_free (&str);
-          
-      if (sresult == FALSE || lport <= 0 || lport > 65535)
-        {
-          _dbus_set_bad_address (error, NULL, NULL,
-                                 "Port is not an integer between 0 and 65535");
-          return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
-        }
-          
-      *transport_p = _dbus_transport_new_for_tcp_socket (host, lport, error);
+      *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, error);
       if (*transport_p == NULL)
         {
           _DBUS_ASSERT_ERROR_IS_SET (error);
index 8cfc543..8a00ab5 100644 (file)
@@ -31,7 +31,8 @@ DBusTransport*          _dbus_transport_new_for_socket     (int                f
                                                             const DBusString  *server_guid,
                                                             const DBusString  *address);
 DBusTransport*          _dbus_transport_new_for_tcp_socket (const char        *host,
-                                                            dbus_int32_t       port,
+                                                            const char        *port,
+                                                            const char        *family,
                                                             DBusError         *error);
 DBusTransportOpenResult _dbus_transport_open_socket        (DBusAddressEntry  *entry,
                                                             DBusTransport    **transport_p,