Migrating with IPv6 address exists problem, gethostbyname()/inet_aton() could not translate IPv6 address/port simply, so use getaddrinfo() in tcp_start_common to translate network address and service. We can get an address list by getaddrinfo(). Userlevel IPv6 Programming Introduction: http://www.akkadia.org/drepper/userapi-ipv6.html Reference RFC 3493, Basic Socket Interface Extensions for IPv6 Signed-off-by: Amos Kong <akong@xxxxxxxxxx> --- net.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 60 insertions(+), 21 deletions(-) diff --git a/net.c b/net.c index da2a8d4..de1db8c 100644 --- a/net.c +++ b/net.c @@ -99,7 +99,7 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) return 0; } -static int tcp_server_bind(int fd, struct sockaddr_in *saddr) +static int tcp_server_bind(int fd, struct addrinfo *rp) { int ret; int val = 1; @@ -107,7 +107,7 @@ static int tcp_server_bind(int fd, struct sockaddr_in *saddr) /* allow fast reuse */ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); - ret = bind(fd, (struct sockaddr *)saddr, sizeof(*saddr)); + ret = bind(fd, rp->ai_addr, rp->ai_addrlen); if (ret == -1) { ret = -socket_error(); @@ -116,12 +116,12 @@ static int tcp_server_bind(int fd, struct sockaddr_in *saddr) } -static int tcp_client_connect(int fd, struct sockaddr_in *saddr) +static int tcp_client_connect(int fd, struct addrinfo *rp) { int ret; do { - ret = connect(fd, (struct sockaddr *)saddr, sizeof(*saddr)); + ret = connect(fd, rp->ai_addr, rp->ai_addrlen); if (ret == -1) { ret = -socket_error(); } @@ -132,36 +132,75 @@ static int tcp_client_connect(int fd, struct sockaddr_in *saddr) static int tcp_start_common(const char *str, int *fd, bool server) { + char hostname[512]; + const char *service; + const char *name; + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + int sfd; int ret = -EINVAL; - struct sockaddr_in saddr; - if (parse_host_port(&saddr, str) < 0) { + *fd = -1; + service = str; + + if (get_str_sep(hostname, sizeof(hostname), &service, ':') < 0) { + error_report("invalid host/port combination: %s", str); return -EINVAL; } - - *fd = qemu_socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - return -1; + if (server && strlen(hostname) == 0) { + name = NULL; + } else { + name = hostname; } - socket_set_nonblock(*fd); + + /* Obtain address(es) matching host/port */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ if (server) { - ret = tcp_server_bind(*fd, &saddr); - } else { - ret = tcp_client_connect(*fd, &saddr); + hints.ai_flags = AI_PASSIVE; } -#ifdef _WIN32 - if (ret == -WSAEALREADY || ret == -WSAEINVAL) { - return ret; /* Success */ + s = getaddrinfo(name, service, &hints, &result); + if (s != 0) { + error_report("qemu: getaddrinfo: %s", gai_strerror(s)); + return -EINVAL; } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind/connect). + If socket(2) (or bind/connect(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = qemu_socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) { + ret = -errno; + continue; + } + socket_set_nonblock(sfd); + if (server) { + ret = tcp_server_bind(sfd, rp); + } else { + ret = tcp_client_connect(sfd, rp); + } +#ifdef _WIN32 + if (ret == -WSAEALREADY || ret == -WSAEINVAL) { + *fd = sfd; + break; /* Success */ + } #endif - if (ret >= 0 || ret == -EINPROGRESS || ret == -EWOULDBLOCK) { - return ret; /* Success */ + if (ret >= 0 || ret == -EINPROGRESS || ret == -EWOULDBLOCK) { + *fd = sfd; + break; /* Success */ + } + closesocket(sfd); } - closesocket(*fd); + freeaddrinfo(result); return ret; } -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html