Patchwork migration: address handling cleanup

login
register
mail settings
Submitter Amos Kong
Date Sept. 11, 2012, 7:33 a.m.
Message ID <1347348830-10338-1-git-send-email-akong@redhat.com>
Download mbox | patch
Permalink /patch/183039/
State New
Headers show

Comments

Amos Kong - Sept. 11, 2012, 7:33 a.m.
From: Michael S. Tsirkin <mst@redhat.com>

getaddrinfo can give us a list of addresses, but we only try to
connect to the first one. If that fails we never proceed to
the next one.  This is common on desktop setups that often have ipv6
configured but not actually working.

To fix this, refactor address resolution code and make tcp migration
retry connection with a different address.

This also drops argument from inet_connect.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
---
 migration-tcp.c |   70 +++++++++++++++++++------
 migration.c     |    5 ++
 migration.h     |    3 +
 nbd.c           |    2 +-
 qemu-sockets.c  |  153 ++++++++++++++++++++++++++++++++++---------------------
 qemu_socket.h   |    6 ++-
 ui/vnc.c        |    2 +-
 7 files changed, 163 insertions(+), 78 deletions(-)
Orit Wasserman - Sept. 11, 2012, 8:39 a.m.
On 09/11/2012 10:33 AM, Amos Kong wrote:
> From: Michael S. Tsirkin <mst@redhat.com>
> 
> getaddrinfo can give us a list of addresses, but we only try to
> connect to the first one. If that fails we never proceed to
> the next one.  This is common on desktop setups that often have ipv6
> configured but not actually working.
> 
> To fix this, refactor address resolution code and make tcp migration
> retry connection with a different address.
> 
> This also drops argument from inet_connect.
> 
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Amos Kong <akong@redhat.com>
> ---
>  migration-tcp.c |   70 +++++++++++++++++++------
>  migration.c     |    5 ++
>  migration.h     |    3 +
>  nbd.c           |    2 +-
>  qemu-sockets.c  |  153 ++++++++++++++++++++++++++++++++++---------------------
>  qemu_socket.h   |    6 ++-
>  ui/vnc.c        |    2 +-
>  7 files changed, 163 insertions(+), 78 deletions(-)
> 
> diff --git a/migration-tcp.c b/migration-tcp.c
> index ac891c3..1c1996b 100644
> --- a/migration-tcp.c
> +++ b/migration-tcp.c
> @@ -30,6 +30,8 @@
>      do { } while (0)
>  #endif
>  
> +static void tcp_wait_for_connect(void *opaque);
> +
>  static int socket_errno(MigrationState *s)
>  {
>      return socket_error();
> @@ -53,13 +55,43 @@ static int tcp_close(MigrationState *s)
>      return r;
>  }
>  
> +/*
> + * Try to connect to next address on list.
> + */
> +static void
> +tcp_next_connect_migration(MigrationState *s, bool *in_progress, Error **errp)
> +{
> +    *in_progress = false;
> +    migrate_fd_retry(s);
> +    while (s->next_addr) {
> +        s->fd = inet_connect_addr(s->next_addr, false, in_progress, errp);
> +        s->next_addr = s->next_addr->ai_next;
> +        if (*in_progress) {
> +            qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
> +            return;
> +        }
> +    }
> +}
> +
Hi,
This code is not migration specific, it should be part of qemu-sockets.c.
I really don't see a good reason to add next_addr to migration state.
I think we need to add a callback for non-blocking connect that is called when the operation is done
(it will need the status of the operation), another option is two callback one for success and one for error.
I personally would create a separate function inet_connect_nonblocking but that is up to you.
Regards,
Orit
> +static int tcp_connect_done(MigrationState *s)
> +{
> +    freeaddrinfo(s->addr_list);
> +    if (s->fd < 0) {
> +        migrate_fd_error(s);
> +        return -1;
> +    }
> +
> +    migrate_fd_connect(s);
> +    return 0;
> +}
> +
>  static void tcp_wait_for_connect(void *opaque)
>  {
>      MigrationState *s = opaque;
>      int val, ret;
> +    bool in_progress;
>      socklen_t valsize = sizeof(val);
>  
> -    DPRINTF("connect completed\n");
>      do {
>          ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
>      } while (ret == -1 && (socket_error()) == EINTR);
> @@ -69,39 +101,45 @@ static void tcp_wait_for_connect(void *opaque)
>          return;
>      }
>  
> +    DPRINTF("connect completed: %d\n", val);
> +
>      qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
>  
> -    if (val == 0)
> -        migrate_fd_connect(s);
> -    else {
> -        DPRINTF("error connecting %d\n", val);
> -        migrate_fd_error(s);
> +    if (val != 0) {
> +        if (!s->next_addr) {
> +            DPRINTF("all addresses could not be connected\n");
> +            freeaddrinfo(s->addr_list);
> +            migrate_fd_error(s);
> +            return;
> +        }
> +
> +        tcp_next_connect_migration(s, &in_progress, NULL);
> +        if (in_progress) {
> +            return;
> +        }
>      }
> +    tcp_connect_done(s);
>  }
>  
>  int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
>                                   Error **errp)
>  {
> -    bool in_progress;
> +    bool in_progress = false;
>  
>      s->get_error = socket_errno;
>      s->write = socket_write;
>      s->close = tcp_close;
> +    s->next_addr = s->addr_list = inet_parse_connect(host_port, errp);
>  
> -    s->fd = inet_connect(host_port, false, &in_progress, errp);
> -    if (error_is_set(errp)) {
> -        migrate_fd_error(s);
> -        return -1;
> -    }
> -
> +    s->fd = inet_connect_addr(s->next_addr, false, &in_progress, errp);
> +    s->next_addr = s->next_addr->ai_next;
>      if (in_progress) {
>          DPRINTF("connect in progress\n");
>          qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
> -    } else {
> -        migrate_fd_connect(s);
> +        return 0;
>      }
>  
> -    return 0;
> +    return tcp_connect_done(s);
>  }
>  
>  static void tcp_accept_incoming_migration(void *opaque)
> diff --git a/migration.c b/migration.c
> index 1edeec5..04c3057 100644
> --- a/migration.c
> +++ b/migration.c
> @@ -256,6 +256,11 @@ static int migrate_fd_cleanup(MigrationState *s)
>      return ret;
>  }
>  
> +int migrate_fd_retry(MigrationState *s)
> +{
> +    return migrate_fd_cleanup(s);
> +}
> +
>  void migrate_fd_error(MigrationState *s)
>  {
>      DPRINTF("setting error state\n");
> diff --git a/migration.h b/migration.h
> index a9852fc..23eb15f 100644
> --- a/migration.h
> +++ b/migration.h
> @@ -33,6 +33,8 @@ struct MigrationState
>      int64_t bandwidth_limit;
>      QEMUFile *file;
>      int fd;
> +    struct addrinfo *addr_list;
> +    struct addrinfo *next_addr;
>      int state;
>      int (*get_error)(MigrationState *s);
>      int (*close)(MigrationState *s);
> @@ -72,6 +74,7 @@ int fd_start_incoming_migration(const char *path);
>  int fd_start_outgoing_migration(MigrationState *s, const char *fdname);
>  
>  void migrate_fd_error(MigrationState *s);
> +int migrate_fd_retry(MigrationState *s);
>  
>  void migrate_fd_connect(MigrationState *s);
>  
> diff --git a/nbd.c b/nbd.c
> index 0dd60c5..417412a 100644
> --- a/nbd.c
> +++ b/nbd.c
> @@ -162,7 +162,7 @@ int tcp_socket_outgoing(const char *address, uint16_t port)
>  
>  int tcp_socket_outgoing_spec(const char *address_and_port)
>  {
> -    return inet_connect(address_and_port, true, NULL, NULL);
> +    return inet_connect(address_and_port, NULL, NULL);
>  }
>  
>  int tcp_socket_incoming(const char *address, uint16_t port)
> diff --git a/qemu-sockets.c b/qemu-sockets.c
> index 361d890..26d754c 100644
> --- a/qemu-sockets.c
> +++ b/qemu-sockets.c
> @@ -209,32 +209,24 @@ listen:
>      return slisten;
>  }
>  
> -int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp)
> +static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
>  {
> -    struct addrinfo ai,*res,*e;
> +    struct addrinfo ai, *res;
> +    int rc;
>      const char *addr;
>      const char *port;
> -    char uaddr[INET6_ADDRSTRLEN+1];
> -    char uport[33];
> -    int sock,rc;
> -    bool block;
>  
>      memset(&ai,0, sizeof(ai));
>      ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
>      ai.ai_family = PF_UNSPEC;
>      ai.ai_socktype = SOCK_STREAM;
>  
> -    if (in_progress) {
> -        *in_progress = false;
> -    }
> -
>      addr = qemu_opt_get(opts, "host");
>      port = qemu_opt_get(opts, "port");
> -    block = qemu_opt_get_bool(opts, "block", 0);
>      if (addr == NULL || port == NULL) {
>          fprintf(stderr, "inet_connect: host and/or port not specified\n");
>          error_set(errp, QERR_SOCKET_CREATE_FAILED);
> -        return -1;
> +        return NULL;
>      }
>  
>      if (qemu_opt_get_bool(opts, "ipv4", 0))
> @@ -247,57 +239,85 @@ int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp)
>          fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
>                  gai_strerror(rc));
>          error_set(errp, QERR_SOCKET_CREATE_FAILED);
> -	return -1;
> +        return NULL;
>      }
> +    return res;
> +}
>  
> -    for (e = res; e != NULL; e = e->ai_next) {
> -        if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
> -                            uaddr,INET6_ADDRSTRLEN,uport,32,
> -                            NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
> -            fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
> -            continue;
> -        }
> -        sock = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
> -        if (sock < 0) {
> -            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
> -            inet_strfamily(e->ai_family), strerror(errno));
> -            continue;
> +#ifdef _WIN32
> +#define QEMU_SOCKET_RC_INPROGRESS(rc) \
> +    ((rc) == -EINPROGRESS || rc == -EWOULDBLOCK || rc == -WSAEALREADY)
> +#else
> +#define QEMU_SOCKET_RC_INPROGRESS(rc) \
> +    ((rc) == -EINPROGRESS)
> +#endif
> +
> +int inet_connect_addr(struct addrinfo *addr, bool block, bool *in_progress,
> +                      Error **errp)
> +{
> +    char uaddr[INET6_ADDRSTRLEN + 1];
> +    char uport[33];
> +    int sock, rc;
> +
> +    if (getnameinfo((struct sockaddr *)addr->ai_addr, addr->ai_addrlen,
> +                    uaddr, INET6_ADDRSTRLEN, uport, 32,
> +                    NI_NUMERICHOST | NI_NUMERICSERV)) {
> +        fprintf(stderr, "%s: getnameinfo: oops\n", __func__);
> +        return -1;
> +    }
> +    sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
> +    if (sock < 0) {
> +        fprintf(stderr, "%s: socket(%s): %s\n", __func__,
> +                inet_strfamily(addr->ai_family), strerror(errno));
> +        return -1;
> +    }
> +    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
> +    if (!block) {
> +        socket_set_nonblock(sock);
> +    }
> +    /* connect to peer */
> +    do {
> +        rc = 0;
> +        if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) {
> +            rc = -socket_error();
>          }
> -        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
> -        if (!block) {
> -            socket_set_nonblock(sock);
> +    } while (rc == -EINTR);
> +
> +    if (!block && QEMU_SOCKET_RC_INPROGRESS(rc)) {
> +        if (in_progress) {
> +            *in_progress = true;
>          }
> -        /* connect to peer */
> -        do {
> -            rc = 0;
> -            if (connect(sock, e->ai_addr, e->ai_addrlen) < 0) {
> -                rc = -socket_error();
> -            }
> -        } while (rc == -EINTR);
> -
> -  #ifdef _WIN32
> -        if (!block && (rc == -EINPROGRESS || rc == -EWOULDBLOCK
> -                       || rc == -WSAEALREADY)) {
> -  #else
> -        if (!block && (rc == -EINPROGRESS)) {
> -  #endif
> -            if (in_progress) {
> -                *in_progress = true;
> -            }
> -        } else if (rc < 0) {
> -            if (NULL == e->ai_next)
> -                fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
> -                        inet_strfamily(e->ai_family),
> -                        e->ai_canonname, uaddr, uport, strerror(errno));
> -            closesocket(sock);
> -            continue;
> +    } else if (rc < 0) {
> +        closesocket(sock);
> +        return -1;
> +    }
> +    return sock;
> +}
> +
> +int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp)
> +{
> +    struct addrinfo *res, *e;
> +    int sock = -1;
> +    bool block = qemu_opt_get_bool(opts, "block", 0);
> +
> +    res = inet_parse_connect_opts(opts, errp);
> +    if (!res) {
> +        return -1;
> +    }
> +
> +    if (in_progress) {
> +        *in_progress = false;
> +    }
> +
> +    for (e = res; e != NULL; e = e->ai_next) {
> +        sock = inet_connect_addr(e, block, in_progress, errp);
> +        if (sock >= 0) {
> +            break;
>          }
> -        freeaddrinfo(res);
> -        return sock;
>      }
>      error_set(errp, QERR_SOCKET_CONNECT_FAILED);
>      freeaddrinfo(res);
> -    return -1;
> +    return sock;
>  }
>  
>  int inet_dgram_opts(QemuOpts *opts)
> @@ -493,16 +513,31 @@ int inet_listen(const char *str, char *ostr, int olen,
>      return sock;
>  }
>  
> -int inet_connect(const char *str, bool block, bool *in_progress, Error **errp)
> +struct addrinfo *inet_parse_connect(const char *str, Error **errp)
> +{
> +    QemuOpts *opts;
> +    struct addrinfo *res;
> +
> +    opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
> +    if (inet_parse(opts, str) == 0) {
> +        qemu_opt_set(opts, "block", "on");
> +        res = inet_parse_connect_opts(opts, errp);
> +    } else {
> +        error_set(errp, QERR_SOCKET_CREATE_FAILED);
> +        res = NULL;
> +    }
> +    qemu_opts_del(opts);
> +    return res;
> +}
> +
> +int inet_connect(const char *str, bool *in_progress, Error **errp)
>  {
>      QemuOpts *opts;
>      int sock = -1;
>  
>      opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
>      if (inet_parse(opts, str) == 0) {
> -        if (block) {
> -            qemu_opt_set(opts, "block", "on");
> -        }
> +        qemu_opt_set(opts, "block", "on");
>          sock = inet_connect_opts(opts, in_progress, errp);
>      } else {
>          error_set(errp, QERR_SOCKET_CREATE_FAILED);
> diff --git a/qemu_socket.h b/qemu_socket.h
> index 30ae6af..a69bfb8 100644
> --- a/qemu_socket.h
> +++ b/qemu_socket.h
> @@ -43,7 +43,11 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp);
>  int inet_listen(const char *str, char *ostr, int olen,
>                  int socktype, int port_offset, Error **errp);
>  int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp);
> -int inet_connect(const char *str, bool block, bool *in_progress, Error **errp);
> +struct addrinfo *inet_parse_connect(const char *str, Error **errp);
> +int inet_connect_addr(struct addrinfo *addr, bool block, bool *in_progress,
> +                      Error **errp);
> +
> +int inet_connect(const char *str, bool *in_progress, Error **errp);
>  int inet_dgram_opts(QemuOpts *opts);
>  const char *inet_strfamily(int family);
>  
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 385e345..5ab8ecc 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -3061,7 +3061,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
>          if (strncmp(display, "unix:", 5) == 0)
>              vs->lsock = unix_connect(display+5);
>          else
> -            vs->lsock = inet_connect(display, true, NULL, NULL);
> +            vs->lsock = inet_connect(display, NULL, NULL);
>          if (-1 == vs->lsock) {
>              g_free(vs->display);
>              vs->display = NULL;
>

Patch

diff --git a/migration-tcp.c b/migration-tcp.c
index ac891c3..1c1996b 100644
--- a/migration-tcp.c
+++ b/migration-tcp.c
@@ -30,6 +30,8 @@ 
     do { } while (0)
 #endif
 
+static void tcp_wait_for_connect(void *opaque);
+
 static int socket_errno(MigrationState *s)
 {
     return socket_error();
@@ -53,13 +55,43 @@  static int tcp_close(MigrationState *s)
     return r;
 }
 
+/*
+ * Try to connect to next address on list.
+ */
+static void
+tcp_next_connect_migration(MigrationState *s, bool *in_progress, Error **errp)
+{
+    *in_progress = false;
+    migrate_fd_retry(s);
+    while (s->next_addr) {
+        s->fd = inet_connect_addr(s->next_addr, false, in_progress, errp);
+        s->next_addr = s->next_addr->ai_next;
+        if (*in_progress) {
+            qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
+            return;
+        }
+    }
+}
+
+static int tcp_connect_done(MigrationState *s)
+{
+    freeaddrinfo(s->addr_list);
+    if (s->fd < 0) {
+        migrate_fd_error(s);
+        return -1;
+    }
+
+    migrate_fd_connect(s);
+    return 0;
+}
+
 static void tcp_wait_for_connect(void *opaque)
 {
     MigrationState *s = opaque;
     int val, ret;
+    bool in_progress;
     socklen_t valsize = sizeof(val);
 
-    DPRINTF("connect completed\n");
     do {
         ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
     } while (ret == -1 && (socket_error()) == EINTR);
@@ -69,39 +101,45 @@  static void tcp_wait_for_connect(void *opaque)
         return;
     }
 
+    DPRINTF("connect completed: %d\n", val);
+
     qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
 
-    if (val == 0)
-        migrate_fd_connect(s);
-    else {
-        DPRINTF("error connecting %d\n", val);
-        migrate_fd_error(s);
+    if (val != 0) {
+        if (!s->next_addr) {
+            DPRINTF("all addresses could not be connected\n");
+            freeaddrinfo(s->addr_list);
+            migrate_fd_error(s);
+            return;
+        }
+
+        tcp_next_connect_migration(s, &in_progress, NULL);
+        if (in_progress) {
+            return;
+        }
     }
+    tcp_connect_done(s);
 }
 
 int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
                                  Error **errp)
 {
-    bool in_progress;
+    bool in_progress = false;
 
     s->get_error = socket_errno;
     s->write = socket_write;
     s->close = tcp_close;
+    s->next_addr = s->addr_list = inet_parse_connect(host_port, errp);
 
-    s->fd = inet_connect(host_port, false, &in_progress, errp);
-    if (error_is_set(errp)) {
-        migrate_fd_error(s);
-        return -1;
-    }
-
+    s->fd = inet_connect_addr(s->next_addr, false, &in_progress, errp);
+    s->next_addr = s->next_addr->ai_next;
     if (in_progress) {
         DPRINTF("connect in progress\n");
         qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
-    } else {
-        migrate_fd_connect(s);
+        return 0;
     }
 
-    return 0;
+    return tcp_connect_done(s);
 }
 
 static void tcp_accept_incoming_migration(void *opaque)
diff --git a/migration.c b/migration.c
index 1edeec5..04c3057 100644
--- a/migration.c
+++ b/migration.c
@@ -256,6 +256,11 @@  static int migrate_fd_cleanup(MigrationState *s)
     return ret;
 }
 
+int migrate_fd_retry(MigrationState *s)
+{
+    return migrate_fd_cleanup(s);
+}
+
 void migrate_fd_error(MigrationState *s)
 {
     DPRINTF("setting error state\n");
diff --git a/migration.h b/migration.h
index a9852fc..23eb15f 100644
--- a/migration.h
+++ b/migration.h
@@ -33,6 +33,8 @@  struct MigrationState
     int64_t bandwidth_limit;
     QEMUFile *file;
     int fd;
+    struct addrinfo *addr_list;
+    struct addrinfo *next_addr;
     int state;
     int (*get_error)(MigrationState *s);
     int (*close)(MigrationState *s);
@@ -72,6 +74,7 @@  int fd_start_incoming_migration(const char *path);
 int fd_start_outgoing_migration(MigrationState *s, const char *fdname);
 
 void migrate_fd_error(MigrationState *s);
+int migrate_fd_retry(MigrationState *s);
 
 void migrate_fd_connect(MigrationState *s);
 
diff --git a/nbd.c b/nbd.c
index 0dd60c5..417412a 100644
--- a/nbd.c
+++ b/nbd.c
@@ -162,7 +162,7 @@  int tcp_socket_outgoing(const char *address, uint16_t port)
 
 int tcp_socket_outgoing_spec(const char *address_and_port)
 {
-    return inet_connect(address_and_port, true, NULL, NULL);
+    return inet_connect(address_and_port, NULL, NULL);
 }
 
 int tcp_socket_incoming(const char *address, uint16_t port)
diff --git a/qemu-sockets.c b/qemu-sockets.c
index 361d890..26d754c 100644
--- a/qemu-sockets.c
+++ b/qemu-sockets.c
@@ -209,32 +209,24 @@  listen:
     return slisten;
 }
 
-int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp)
+static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
 {
-    struct addrinfo ai,*res,*e;
+    struct addrinfo ai, *res;
+    int rc;
     const char *addr;
     const char *port;
-    char uaddr[INET6_ADDRSTRLEN+1];
-    char uport[33];
-    int sock,rc;
-    bool block;
 
     memset(&ai,0, sizeof(ai));
     ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
     ai.ai_family = PF_UNSPEC;
     ai.ai_socktype = SOCK_STREAM;
 
-    if (in_progress) {
-        *in_progress = false;
-    }
-
     addr = qemu_opt_get(opts, "host");
     port = qemu_opt_get(opts, "port");
-    block = qemu_opt_get_bool(opts, "block", 0);
     if (addr == NULL || port == NULL) {
         fprintf(stderr, "inet_connect: host and/or port not specified\n");
         error_set(errp, QERR_SOCKET_CREATE_FAILED);
-        return -1;
+        return NULL;
     }
 
     if (qemu_opt_get_bool(opts, "ipv4", 0))
@@ -247,57 +239,85 @@  int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp)
         fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
                 gai_strerror(rc));
         error_set(errp, QERR_SOCKET_CREATE_FAILED);
-	return -1;
+        return NULL;
     }
+    return res;
+}
 
-    for (e = res; e != NULL; e = e->ai_next) {
-        if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
-                            uaddr,INET6_ADDRSTRLEN,uport,32,
-                            NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
-            fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
-            continue;
-        }
-        sock = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
-        if (sock < 0) {
-            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
-            inet_strfamily(e->ai_family), strerror(errno));
-            continue;
+#ifdef _WIN32
+#define QEMU_SOCKET_RC_INPROGRESS(rc) \
+    ((rc) == -EINPROGRESS || rc == -EWOULDBLOCK || rc == -WSAEALREADY)
+#else
+#define QEMU_SOCKET_RC_INPROGRESS(rc) \
+    ((rc) == -EINPROGRESS)
+#endif
+
+int inet_connect_addr(struct addrinfo *addr, bool block, bool *in_progress,
+                      Error **errp)
+{
+    char uaddr[INET6_ADDRSTRLEN + 1];
+    char uport[33];
+    int sock, rc;
+
+    if (getnameinfo((struct sockaddr *)addr->ai_addr, addr->ai_addrlen,
+                    uaddr, INET6_ADDRSTRLEN, uport, 32,
+                    NI_NUMERICHOST | NI_NUMERICSERV)) {
+        fprintf(stderr, "%s: getnameinfo: oops\n", __func__);
+        return -1;
+    }
+    sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+    if (sock < 0) {
+        fprintf(stderr, "%s: socket(%s): %s\n", __func__,
+                inet_strfamily(addr->ai_family), strerror(errno));
+        return -1;
+    }
+    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+    if (!block) {
+        socket_set_nonblock(sock);
+    }
+    /* connect to peer */
+    do {
+        rc = 0;
+        if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) {
+            rc = -socket_error();
         }
-        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
-        if (!block) {
-            socket_set_nonblock(sock);
+    } while (rc == -EINTR);
+
+    if (!block && QEMU_SOCKET_RC_INPROGRESS(rc)) {
+        if (in_progress) {
+            *in_progress = true;
         }
-        /* connect to peer */
-        do {
-            rc = 0;
-            if (connect(sock, e->ai_addr, e->ai_addrlen) < 0) {
-                rc = -socket_error();
-            }
-        } while (rc == -EINTR);
-
-  #ifdef _WIN32
-        if (!block && (rc == -EINPROGRESS || rc == -EWOULDBLOCK
-                       || rc == -WSAEALREADY)) {
-  #else
-        if (!block && (rc == -EINPROGRESS)) {
-  #endif
-            if (in_progress) {
-                *in_progress = true;
-            }
-        } else if (rc < 0) {
-            if (NULL == e->ai_next)
-                fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
-                        inet_strfamily(e->ai_family),
-                        e->ai_canonname, uaddr, uport, strerror(errno));
-            closesocket(sock);
-            continue;
+    } else if (rc < 0) {
+        closesocket(sock);
+        return -1;
+    }
+    return sock;
+}
+
+int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp)
+{
+    struct addrinfo *res, *e;
+    int sock = -1;
+    bool block = qemu_opt_get_bool(opts, "block", 0);
+
+    res = inet_parse_connect_opts(opts, errp);
+    if (!res) {
+        return -1;
+    }
+
+    if (in_progress) {
+        *in_progress = false;
+    }
+
+    for (e = res; e != NULL; e = e->ai_next) {
+        sock = inet_connect_addr(e, block, in_progress, errp);
+        if (sock >= 0) {
+            break;
         }
-        freeaddrinfo(res);
-        return sock;
     }
     error_set(errp, QERR_SOCKET_CONNECT_FAILED);
     freeaddrinfo(res);
-    return -1;
+    return sock;
 }
 
 int inet_dgram_opts(QemuOpts *opts)
@@ -493,16 +513,31 @@  int inet_listen(const char *str, char *ostr, int olen,
     return sock;
 }
 
-int inet_connect(const char *str, bool block, bool *in_progress, Error **errp)
+struct addrinfo *inet_parse_connect(const char *str, Error **errp)
+{
+    QemuOpts *opts;
+    struct addrinfo *res;
+
+    opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
+    if (inet_parse(opts, str) == 0) {
+        qemu_opt_set(opts, "block", "on");
+        res = inet_parse_connect_opts(opts, errp);
+    } else {
+        error_set(errp, QERR_SOCKET_CREATE_FAILED);
+        res = NULL;
+    }
+    qemu_opts_del(opts);
+    return res;
+}
+
+int inet_connect(const char *str, bool *in_progress, Error **errp)
 {
     QemuOpts *opts;
     int sock = -1;
 
     opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
     if (inet_parse(opts, str) == 0) {
-        if (block) {
-            qemu_opt_set(opts, "block", "on");
-        }
+        qemu_opt_set(opts, "block", "on");
         sock = inet_connect_opts(opts, in_progress, errp);
     } else {
         error_set(errp, QERR_SOCKET_CREATE_FAILED);
diff --git a/qemu_socket.h b/qemu_socket.h
index 30ae6af..a69bfb8 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -43,7 +43,11 @@  int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp);
 int inet_listen(const char *str, char *ostr, int olen,
                 int socktype, int port_offset, Error **errp);
 int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp);
-int inet_connect(const char *str, bool block, bool *in_progress, Error **errp);
+struct addrinfo *inet_parse_connect(const char *str, Error **errp);
+int inet_connect_addr(struct addrinfo *addr, bool block, bool *in_progress,
+                      Error **errp);
+
+int inet_connect(const char *str, bool *in_progress, Error **errp);
 int inet_dgram_opts(QemuOpts *opts);
 const char *inet_strfamily(int family);
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 385e345..5ab8ecc 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3061,7 +3061,7 @@  int vnc_display_open(DisplayState *ds, const char *display)
         if (strncmp(display, "unix:", 5) == 0)
             vs->lsock = unix_connect(display+5);
         else
-            vs->lsock = inet_connect(display, true, NULL, NULL);
+            vs->lsock = inet_connect(display, NULL, NULL);
         if (-1 == vs->lsock) {
             g_free(vs->display);
             vs->display = NULL;