summaryrefslogtreecommitdiffstats
path: root/dbus/dbus-sysdeps-unix.c
diff options
context:
space:
mode:
authorDaniel P. Berrange <dan@berrange.com>2007-07-25 02:46:52 +0000
committerDaniel P. Berrange <dan@berrange.com>2007-07-25 02:46:52 +0000
commitee71e1ff6033ad1eb2385f11d4f3678259b8397b (patch)
treeed47c0cc3273361474d746d7b1952a10da4e60fc /dbus/dbus-sysdeps-unix.c
parent79d3004e26f490ef37ae0298495ea66f322ce374 (diff)
Switch over to using getaddrinfo for TCP clients & servers to enable IPv6
Diffstat (limited to 'dbus/dbus-sysdeps-unix.c')
-rw-r--r--dbus/dbus-sysdeps-unix.c301
1 files changed, 214 insertions, 87 deletions
diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c
index 2ea83df2..0ab5e730 100644
--- a/dbus/dbus-sysdeps-unix.c
+++ b/dbus/dbus-sysdeps-unix.c
@@ -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))
{