Switch over to using getaddrinfo for TCP clients & servers to enable IPv6

This commit is contained in:
Daniel P. Berrange 2007-07-25 02:46:52 +00:00
parent 79d3004e26
commit ee71e1ff60
10 changed files with 473 additions and 250 deletions

View file

@ -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> 2007-07-24 Havoc Pennington <hp@redhat.com>
* configure.in: add AM_PROG_CC_C_O to allow per-target CPPFLAGS * configure.in: add AM_PROG_CC_C_O to allow per-target CPPFLAGS

View file

@ -220,6 +220,15 @@ started services or other interested parties with
the last address given in <listen> first. That is, the last address given in <listen> first. That is,
apps will try to connect to the last <listen> address first. 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 .PP
A special case is using a port number of zero (or omitting the port), 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 which means to choose an available port selected by the operating
@ -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> Example: <listen>tcp:host=localhost,port=0</listen>
.PP .PP
tcp addresses also allow an all_interfaces=true option, which will tcp addresses also allow a bind=hostname option, which will override
cause the bus to listen on all local address (INADDR_ANY) and not only the host option specifying what address to bind to, without changing
the specified host. However, the specified host will still be used as the address reported by the bus. The bind option can also take a
the reported address of the server. The specified host should be a special name '*' to cause the bus to listen on all local address
valid name of the local machine or weird stuff will happen. (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 .TP
.I "<auth>" .I "<auth>"

View file

@ -89,6 +89,7 @@ _dbus_set_bad_address (DBusError *error,
(b) == '_' || \ (b) == '_' || \
(b) == '/' || \ (b) == '/' || \
(b) == '\\' || \ (b) == '\\' || \
(b) == '*' || \
(b) == '.') (b) == '.')
/** /**
@ -691,7 +692,6 @@ static const char* invalid_escaped_values[] = {
"%", "%",
"$", "$",
" ", " ",
"*"
}; };
dbus_bool_t dbus_bool_t

View file

@ -47,8 +47,9 @@ typedef struct DBusServerSocket DBusServerSocket;
struct DBusServerSocket struct DBusServerSocket
{ {
DBusServer base; /**< Parent class members. */ DBusServer base; /**< Parent class members. */
int fd; /**< File descriptor or -1 if disconnected. */ int n_fds; /**< Number of active file handles */
DBusWatch *watch; /**< File descriptor watch. */ int *fds; /**< File descriptor or -1 if disconnected. */
DBusWatch **watch; /**< File descriptor watch. */
char *socket_name; /**< Name of domain socket, to unlink if appropriate */ char *socket_name; /**< Name of domain socket, to unlink if appropriate */
}; };
@ -56,15 +57,19 @@ static void
socket_finalize (DBusServer *server) socket_finalize (DBusServer *server)
{ {
DBusServerSocket *socket_server = (DBusServerSocket*) server; DBusServerSocket *socket_server = (DBusServerSocket*) server;
int i;
_dbus_server_finalize_base (server); _dbus_server_finalize_base (server);
if (socket_server->watch) for (i = 0 ; i < socket_server->n_fds ; i++)
{ if (socket_server->watch[i])
_dbus_watch_unref (socket_server->watch); {
socket_server->watch = NULL; _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 (socket_server->socket_name);
dbus_free (server); dbus_free (server);
} }
@ -149,10 +154,21 @@ socket_handle_watch (DBusWatch *watch,
{ {
DBusServer *server = data; DBusServer *server = data;
DBusServerSocket *socket_server = data; DBusServerSocket *socket_server = data;
#ifndef DBUS_DISABLE_ASSERT
int i;
dbus_bool_t found = FALSE;
#endif
SERVER_LOCK (server); 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); _dbus_verbose ("Handling client connection, flags 0x%x\n", flags);
@ -199,19 +215,23 @@ static void
socket_disconnect (DBusServer *server) socket_disconnect (DBusServer *server)
{ {
DBusServerSocket *socket_server = (DBusServerSocket*) server; DBusServerSocket *socket_server = (DBusServerSocket*) server;
int i;
HAVE_LOCK_CHECK (server); HAVE_LOCK_CHECK (server);
if (socket_server->watch) for (i = 0 ; i < socket_server->n_fds ; i++)
{ {
_dbus_server_remove_watch (server, if (socket_server->watch[i])
socket_server->watch); {
_dbus_watch_unref (socket_server->watch); _dbus_server_remove_watch (server,
socket_server->watch = NULL; 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) 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 * been successfully invoked on it. The server will use accept() to
* accept new client connections. * 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 * @param address the server's address
* @returns the new server, or #NULL if no memory. * @returns the new server, or #NULL if no memory.
* *
*/ */
DBusServer* DBusServer*
_dbus_server_new_for_socket (int fd, _dbus_server_new_for_socket (int *fds,
int n_fds,
const DBusString *address) const DBusString *address)
{ {
DBusServerSocket *socket_server; DBusServerSocket *socket_server;
DBusServer *server; DBusServer *server;
DBusWatch *watch; int i;
socket_server = dbus_new0 (DBusServerSocket, 1); socket_server = dbus_new0 (DBusServerSocket, 1);
if (socket_server == NULL) if (socket_server == NULL)
return NULL; return NULL;
watch = _dbus_watch_new (fd, socket_server->fds = dbus_new (int, n_fds);
DBUS_WATCH_READABLE, if (!socket_server->fds)
TRUE, goto failed_0;
socket_handle_watch, socket_server,
NULL); socket_server->watch = dbus_new0 (DBusWatch *, n_fds);
if (watch == NULL) if (!socket_server->watch)
goto failed_1;
for (i = 0 ; i < n_fds ; i++)
{ {
dbus_free (socket_server); DBusWatch *watch;
return NULL;
} watch = _dbus_watch_new (fds[i],
DBUS_WATCH_READABLE,
if (!_dbus_server_init_base (&socket_server->base, TRUE,
&socket_vtable, address)) socket_handle_watch, socket_server,
{ NULL);
_dbus_watch_unref (watch); if (watch == NULL)
dbus_free (socket_server); goto failed_2;
return NULL;
socket_server->n_fds++;
socket_server->fds[i] = fds[i];
socket_server->watch[i] = watch;
} }
server = (DBusServer*) socket_server; if (!_dbus_server_init_base (&socket_server->base,
&socket_vtable, address))
goto failed_2;
server = (DBusServer*)socket_server;
SERVER_LOCK (server); SERVER_LOCK (server);
if (!_dbus_server_add_watch (&socket_server->base, for (i = 0 ; i < n_fds ; i++)
watch))
{ {
SERVER_UNLOCK (server); if (!_dbus_server_add_watch (&socket_server->base,
_dbus_server_finalize_base (&socket_server->base); socket_server->watch[i]))
_dbus_watch_unref (watch); {
dbus_free (socket_server); int j;
return NULL; 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); SERVER_UNLOCK (server);
return (DBusServer*) socket_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. * Creates a new server listening on TCP.
* If inaddr_any is TRUE, listens on all local interfaces. * If host is NULL, it will default to localhost.
* Otherwise, it resolves the hostname and listens only on * If bind is NULL, it will default to the value for the host
* the resolved address of the hostname. The hostname is used * parameter, and if that is NULL, then localhost
* even if inaddr_any is TRUE, as the hostname to report when * If bind is a hostname, it will be resolved and will listen
* dbus_server_get_address() is called. If the hostname is #NULL, * on all returned addresses.
* localhost is used. * 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 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. * @param error location to store reason for failure.
* @returns the new server, or #NULL on failure. * @returns the new server, or #NULL on failure.
*/ */
DBusServer* DBusServer*
_dbus_server_new_for_tcp_socket (const char *host, _dbus_server_new_for_tcp_socket (const char *host,
dbus_uint32_t port, const char *bind,
dbus_bool_t inaddr_any, const char *port,
const char *family,
DBusError *error) DBusError *error)
{ {
DBusServer *server; DBusServer *server;
int listen_fd; int *listen_fds = NULL;
int nlisten_fds = 0, i;
DBusString address; DBusString address;
DBusString host_str; DBusString host_str;
DBusString port_str;
_DBUS_ASSERT_ERROR_IS_CLEAR (error); _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@ -328,44 +387,77 @@ _dbus_server_new_for_tcp_socket (const char *host,
return NULL; return NULL;
} }
if (!_dbus_string_init (&port_str))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto failed_0;
}
if (host == NULL) if (host == NULL)
host = "localhost"; host = "localhost";
listen_fd = _dbus_listen_tcp_socket (host, &port, inaddr_any, error); if (port == NULL)
_dbus_fd_set_close_on_exec (listen_fd); 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); _dbus_string_init_const (&host_str, host);
if (!_dbus_string_append (&address, "tcp:host=") || if (!_dbus_string_append (&address, "tcp:host=") ||
!_dbus_address_append_escaped (&address, &host_str) || !_dbus_address_append_escaped (&address, &host_str) ||
!_dbus_string_append (&address, ",port=") || !_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); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return NULL; goto failed_2;
} }
if (family &&
(!_dbus_string_append (&address, ",family=") ||
if (listen_fd < 0) !_dbus_string_append (&address, family)))
{ {
_dbus_string_free (&address); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return 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) if (server == NULL)
{ {
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
_dbus_close_socket (listen_fd, NULL); goto failed_2;
_dbus_string_free (&address);
return NULL;
} }
_dbus_string_free (&port_str);
_dbus_string_free (&address); _dbus_string_free (&address);
dbus_free(listen_fds);
return server; 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 *host;
const char *port; const char *port;
const char *all_interfaces; const char *bind;
dbus_bool_t inaddr_any; const char *family;
long lport;
host = dbus_address_entry_get_value (entry, "host"); host = dbus_address_entry_get_value (entry, "host");
bind = dbus_address_entry_get_value (entry, "bind");
port = dbus_address_entry_get_value (entry, "port"); 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; *server_p = _dbus_server_new_for_tcp_socket (host, bind, port,
if (all_interfaces != NULL) family, error);
{
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);
if (*server_p) if (*server_p)
{ {

View file

@ -28,11 +28,13 @@
DBUS_BEGIN_DECLS 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); const DBusString *address);
DBusServer* _dbus_server_new_for_tcp_socket (const char *host, DBusServer* _dbus_server_new_for_tcp_socket (const char *host,
dbus_uint32_t port, const char *bind,
dbus_bool_t inaddr_any, const char *port,
const char *family,
DBusError *error); DBusError *error);
DBusServerListenResult _dbus_server_listen_socket (DBusAddressEntry *entry, DBusServerListenResult _dbus_server_listen_socket (DBusAddressEntry *entry,
DBusServer **server_p, DBusServer **server_p,

View file

@ -209,7 +209,7 @@ _dbus_server_new_for_domain_socket (const char *path,
goto failed_1; 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) if (server == NULL)
{ {
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);

View file

@ -737,65 +737,94 @@ _dbus_listen_unix_socket (const char *path,
* nonblocking. * nonblocking.
* *
* @param host the host name to connect to * @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 * @param error return location for error code
* @returns connection file descriptor or -1 on error * @returns connection file descriptor or -1 on error
*/ */
int int
_dbus_connect_tcp_socket (const char *host, _dbus_connect_tcp_socket (const char *host,
dbus_uint32_t port, const char *port,
const char *family,
DBusError *error) DBusError *error)
{ {
int fd; int fd = -1, res;
struct sockaddr_in addr; struct addrinfo hints;
struct hostent *he; struct addrinfo *ai, *tmp;
struct in_addr *haddr;
_DBUS_ASSERT_ERROR_IS_CLEAR (error); _DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (!_dbus_open_tcp_socket (&fd, error)) if (!_dbus_open_tcp_socket (&fd, error))
{ {
_DBUS_ASSERT_ERROR_IS_SET(error); _DBUS_ASSERT_ERROR_IS_SET(error);
return -1; return -1;
} }
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
if (host == NULL)
host = "localhost";
he = gethostbyname (host); _DBUS_ASSERT_ERROR_IS_CLEAR(error);
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_set_error (error,
_dbus_error_from_errno (errno), _dbus_error_from_errno (errno),
"Failed to lookup hostname: %s", "Unknown address family %s", family);
host);
_dbus_close (fd, NULL);
return -1; return -1;
} }
fprintf(stderr, "Family %s\n", family ? family : "none");
haddr = ((struct in_addr *) (he->h_addr_list)[0]); hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
_DBUS_ZERO (addr); if ((res = getaddrinfo(host, port, &hints, &ai)) != 0)
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)
{
dbus_set_error (error, dbus_set_error (error,
_dbus_error_from_errno (errno), _dbus_error_from_errno (errno),
"Failed to connect to socket %s:%d %s", "Failed to lookup host/port: \"%s:%s\": %s (%d)",
host, port, _dbus_strerror (errno)); host, port, gai_strerror(res), res);
_dbus_close (fd, NULL); _dbus_close (fd, NULL);
fd = -1;
return -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)) if (!_dbus_set_fd_nonblocking (fd, error))
{ {
_dbus_close (fd, NULL); _dbus_close (fd, NULL);
@ -814,88 +843,186 @@ _dbus_connect_tcp_socket (const char *host,
* If inaddr_any is specified, the hostname is ignored. * If inaddr_any is specified, the hostname is ignored.
* *
* @param host the host name to listen on * @param host the host name to listen on
* @param port the prot to listen on, if zero a free port will be used * @param port the port 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 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 * @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 int
_dbus_listen_tcp_socket (const char *host, _dbus_listen_tcp_socket (const char *host,
dbus_uint32_t *port, const char *port,
dbus_bool_t inaddr_any, const char *family,
DBusString *retport,
int **fds_p,
DBusError *error) DBusError *error)
{ {
int listen_fd; int nlisten_fd = 0, *listen_fd = NULL, res, i;
struct sockaddr_in addr; struct addrinfo hints;
socklen_t len = (socklen_t) sizeof (struct sockaddr); struct addrinfo *ai, *tmp;
_DBUS_ASSERT_ERROR_IS_CLEAR (error); *fds_p = NULL;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (!_dbus_open_tcp_socket (&listen_fd, error))
{
_DBUS_ASSERT_ERROR_IS_SET(error);
return -1;
}
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
_DBUS_ZERO (addr); _DBUS_ZERO (hints);
if (inaddr_any) if (!family)
{ hints.ai_family = AF_UNSPEC;
addr.sin_addr.s_addr = INADDR_ANY; else if (!strcmp(family, "ipv4"))
} hints.ai_family = AF_INET;
else if (!strcmp(family, "ipv6"))
hints.ai_family = AF_INET6;
else else
{ {
struct hostent *he; dbus_set_error (error,
struct in_addr *haddr; _dbus_error_from_errno (errno),
"Unknown address family %s", family);
return -1;
}
he = gethostbyname (host); hints.ai_protocol = IPPROTO_TCP;
if (he == NULL) 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)
{
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;
}
tmp = ai;
while (tmp)
{
int fd = -1, *newlisten_fd;
if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
{ {
dbus_set_error (error, _DBUS_ASSERT_ERROR_IS_SET(error);
_dbus_error_from_errno (errno), goto failed;
"Failed to lookup hostname: %s",
host);
_dbus_close (listen_fd, NULL);
return -1;
} }
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
haddr = ((struct in_addr *) (he->h_addr_list)[0]);
memcpy (&addr.sin_addr, haddr, sizeof (struct in_addr));
}
addr.sin_family = AF_INET;
addr.sin_port = htons (*port);
if (bind (listen_fd, (struct sockaddr*) &addr, sizeof (struct sockaddr))) if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
{
_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;
}
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;
}
freeaddrinfo(ai);
ai = NULL;
if (!nlisten_fd)
{ {
errno = EADDRINUSE;
dbus_set_error (error, _dbus_error_from_errno (errno), dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to bind socket \"%s:%d\": %s", "Failed to bind socket \"%s:%s\": %s",
host, *port, _dbus_strerror (errno)); host ? host : "*", port, _dbus_strerror (errno));
_dbus_close (listen_fd, NULL);
return -1; 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), if (!_dbus_set_fd_nonblocking (listen_fd[i], error))
"Failed to listen on socket \"%s:%d\": %s", {
host, *port, _dbus_strerror (errno)); goto failed;
_dbus_close (listen_fd, NULL); }
return -1;
} }
getsockname(listen_fd, (struct sockaddr*) &addr, &len); *fds_p = listen_fd;
*port = (dbus_uint32_t) ntohs(addr.sin_port);
if (!_dbus_set_fd_nonblocking (listen_fd, error)) return nlisten_fd;
{
_dbus_close (listen_fd, NULL); failed:
return -1; if (ai)
} freeaddrinfo(ai);
for (i = 0 ; i < nlisten_fd ; i++)
return listen_fd; _dbus_close(listen_fd[i], NULL);
dbus_free(listen_fd);
return -1;
} }
static dbus_bool_t static dbus_bool_t
@ -1089,7 +1216,7 @@ _dbus_read_credentials_socket (int client_fd,
#ifdef SO_PEERCRED #ifdef SO_PEERCRED
struct ucred cr; struct ucred cr;
int cr_len = sizeof (cr); int cr_len = sizeof (cr);
if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
cr_len == sizeof (cr)) cr_len == sizeof (cr))
{ {

View file

@ -144,11 +144,14 @@ int _dbus_write_socket_two (int fd,
int start2, int start2,
int len2); int len2);
int _dbus_connect_tcp_socket (const char *host, int _dbus_connect_tcp_socket (const char *host,
dbus_uint32_t port, const char *port,
const char *family,
DBusError *error); DBusError *error);
int _dbus_listen_tcp_socket (const char *host, int _dbus_listen_tcp_socket (const char *host,
dbus_uint32_t *port, const char *port,
dbus_bool_t inaddr_any, const char *family,
DBusString *retport,
int **fds_p,
DBusError *error); DBusError *error);
int _dbus_accept (int listen_fd); int _dbus_accept (int listen_fd);

View file

@ -1204,15 +1204,18 @@ _dbus_transport_new_for_socket (int fd,
/** /**
* Creates a new transport for the given hostname and port. * 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 host the host to connect to
* @param port the port 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. * @param error location to store reason for failure.
* @returns a new transport, or #NULL on failure. * @returns a new transport, or #NULL on failure.
*/ */
DBusTransport* DBusTransport*
_dbus_transport_new_for_tcp_socket (const char *host, _dbus_transport_new_for_tcp_socket (const char *host,
dbus_int32_t port, const char *port,
const char *family,
DBusError *error) DBusError *error)
{ {
int fd; int fd;
@ -1226,20 +1229,27 @@ _dbus_transport_new_for_tcp_socket (const char *host,
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return NULL; return NULL;
} }
if (host == NULL)
host = "localhost";
if (!_dbus_string_append (&address, "tcp:")) if (!_dbus_string_append (&address, "tcp:"))
goto error; goto error;
if (host != NULL && if (!_dbus_string_append (&address, "host=") ||
(!_dbus_string_append (&address, "host=") || !_dbus_string_append (&address, host))
!_dbus_string_append (&address, host)))
goto error; goto error;
if (!_dbus_string_append (&address, ",port=") || if (!_dbus_string_append (&address, ",port=") ||
!_dbus_string_append_int (&address, port)) !_dbus_string_append (&address, port))
goto error; 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) if (fd < 0)
{ {
_DBUS_ASSERT_ERROR_IS_SET (error); _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_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); host, port);
transport = _dbus_transport_new_for_socket (fd, NULL, &address); 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 *host = dbus_address_entry_get_value (entry, "host");
const char *port = dbus_address_entry_get_value (entry, "port"); const char *port = dbus_address_entry_get_value (entry, "port");
DBusString str; const char *family = dbus_address_entry_get_value (entry, "family");
long lport;
dbus_bool_t sresult;
if (port == NULL) if (port == NULL)
{ {
_dbus_set_bad_address (error, "tcp", "port", NULL); _dbus_set_bad_address (error, "tcp", "port", NULL);
return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
} }
_dbus_string_init_const (&str, port); *transport_p = _dbus_transport_new_for_tcp_socket (host, port, family, error);
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);
if (*transport_p == NULL) if (*transport_p == NULL)
{ {
_DBUS_ASSERT_ERROR_IS_SET (error); _DBUS_ASSERT_ERROR_IS_SET (error);

View file

@ -31,7 +31,8 @@ DBusTransport* _dbus_transport_new_for_socket (int f
const DBusString *server_guid, const DBusString *server_guid,
const DBusString *address); const DBusString *address);
DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host, DBusTransport* _dbus_transport_new_for_tcp_socket (const char *host,
dbus_int32_t port, const char *port,
const char *family,
DBusError *error); DBusError *error);
DBusTransportOpenResult _dbus_transport_open_socket (DBusAddressEntry *entry, DBusTransportOpenResult _dbus_transport_open_socket (DBusAddressEntry *entry,
DBusTransport **transport_p, DBusTransport **transport_p,