diff options
author | Daniel P. Berrange <dan@berrange.com> | 2007-07-25 02:46:52 +0000 |
---|---|---|
committer | Daniel P. Berrange <dan@berrange.com> | 2007-07-25 02:46:52 +0000 |
commit | ee71e1ff6033ad1eb2385f11d4f3678259b8397b (patch) | |
tree | ed47c0cc3273361474d746d7b1952a10da4e60fc /dbus/dbus-sysdeps-unix.c | |
parent | 79d3004e26f490ef37ae0298495ea66f322ce374 (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.c | 301 |
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)) { |