Move out of the SockAddr structure the mutable fields "ai" and
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 8 Nov 2008 16:45:45 +0000 (16:45 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 8 Nov 2008 16:45:45 +0000 (16:45 +0000)
"curraddr", and turn "family" into a macro-derived property of the
other fields. The idea is that this renders SockAddrs immutable once
created, which should open up the possibility of duplicating and
reusing one without having to redo the actual DNS lookup.

I _hope_ I haven't broken anything. The new code architecture
contains several rather dubious-looking operations (namely the
arbitrary choice of the first returned address in functions like
sk_getaddr and sk_address_is_local - what if, for instance, a DNS
lookup returned a local and a non-local address?), but I think they
were functionally just as dubious beforehand and all this change has
done is to make them more obviously so to a reader.

git-svn-id: svn://svn.tartarus.org/sgt/putty@8293 cda61777-01e9-0310-a592-d414129be87e

unix/uxnet.c
windows/winnet.c

index a82c986..98453db 100644 (file)
  */
 typedef struct Socket_tag *Actual_Socket;
 
+/*
+ * Mutable state that goes with a SockAddr: stores information
+ * about where in the list of candidate IP(v*) addresses we've
+ * currently got to.
+ */
+typedef struct SockAddrStep_tag SockAddrStep;
+struct SockAddrStep_tag {
+#ifndef NO_IPV6
+    struct addrinfo *ai;              /* steps along addr->ais */
+#endif
+    int curraddr;
+};
+
 struct Socket_tag {
     struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -64,6 +77,7 @@ struct Socket_tag {
     int nodelay, keepalive;            /* for connect()-type sockets */
     int privport, port;                /* and again */
     SockAddr addr;
+    SockAddrStep step;
     /*
      * We sometimes need pairs of Socket structures to be linked:
      * if we are listening on the same IPv6 and v4 port, for
@@ -75,23 +89,45 @@ struct Socket_tag {
 
 struct SockAddr_tag {
     const char *error;
-    /*
-     * Which address family this address belongs to. AF_INET for
-     * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name
-     * resolution has not been done and a simple host name is held
-     * in this SockAddr structure.
-     */
-    int family;
+    enum { UNRESOLVED, UNIX, IP } superfamily;
 #ifndef NO_IPV6
     struct addrinfo *ais;             /* Addresses IPv6 style. */
-    struct addrinfo *ai;              /* steps along the linked list */
 #else
     unsigned long *addresses;         /* Addresses IPv4 style. */
-    int naddresses, curraddr;
+    int naddresses;
 #endif
     char hostname[512];                       /* Store an unresolved host name. */
 };
 
+/*
+ * Which address family this address belongs to. AF_INET for IPv4;
+ * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has
+ * not been done and a simple host name is held in this SockAddr
+ * structure.
+ */
+#ifndef NO_IPV6
+#define SOCKADDR_FAMILY(addr, step) \
+    ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \
+     (addr)->superfamily == UNIX ? AF_UNIX : \
+     (step).ai ? (step).ai->ai_family : AF_INET)
+#else
+#define SOCKADDR_FAMILY(addr, step) \
+    ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \
+     (addr)->superfamily == UNIX ? AF_UNIX : AF_INET)
+#endif
+
+/*
+ * Start a SockAddrStep structure to step through multiple
+ * addresses.
+ */
+#ifndef NO_IPV6
+#define START_STEP(addr, step) \
+    ((step).ai = (addr)->ais, (step).curraddr = 0)
+#else
+#define START_STEP(addr, step) \
+    ((step).curraddr = 0)
+#endif
+
 static tree234 *sktree;
 
 static void uxsel_tell(Actual_Socket s);
@@ -154,7 +190,7 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_famil
 
     /* Clear the structure and default to IPv4. */
     memset(ret, 0, sizeof(struct SockAddr_tag));
-    ret->family = 0;                  /* We set this one when we have resolved the host. */
+    ret->superfamily = UNRESOLVED;
     *realhost = '\0';
     ret->error = NULL;
 
@@ -170,15 +206,14 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_famil
     hints.ai_canonname = NULL;
     hints.ai_next = NULL;
     err = getaddrinfo(host, NULL, &hints, &ret->ais);
-    ret->ai = ret->ais;
     if (err != 0) {
        ret->error = gai_strerror(err);
        return ret;
     }
-    ret->family = ret->ai->ai_family;
+    ret->superfamily = IP;
     *realhost = '\0';
-    if (ret->ai->ai_canonname != NULL)
-       strncat(realhost, ret->ai->ai_canonname, sizeof(realhost) - 1);
+    if (ret->ais->ai_canonname != NULL)
+       strncat(realhost, ret->ais->ai_canonname, sizeof(realhost) - 1);
     else
        strncat(realhost, host, sizeof(realhost) - 1);
 #else
@@ -187,12 +222,12 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_famil
         * Otherwise use the IPv4-only gethostbyname... (NOTE:
         * we don't use gethostbyname as a fallback!)
         */
-       if (ret->family == 0) {
+       if (ret->superfamily == UNRESOLVED) {
            /*debug(("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host)); */
            if ( (h = gethostbyname(host)) )
-               ret->family = AF_INET;
+               ret->superfamily = IP;
        }
-       if (ret->family == 0) {
+       if (ret->superfamily == UNRESOLVED) {
            ret->error = (h_errno == HOST_NOT_FOUND ||
                          h_errno == NO_DATA ||
                          h_errno == NO_ADDRESS ? "Host does not exist" :
@@ -215,12 +250,11 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_famil
         * This must be a numeric IPv4 address because it caused a
         * success return from inet_addr.
         */
-       ret->family = AF_INET;
+       ret->superfamily = IP;
        strncpy(realhost, host, sizeof(realhost));
        ret->addresses = snew(unsigned long);
        ret->naddresses = 1;
        ret->addresses[0] = ntohl(a);
-       ret->curraddr = 0;
     }
 #endif
     realhost[lenof(realhost)-1] = '\0';
@@ -233,7 +267,7 @@ SockAddr sk_nonamelookup(const char *host)
 {
     SockAddr ret = snew(struct SockAddr_tag);
     ret->error = NULL;
-    ret->family = AF_UNSPEC;
+    ret->superfamily = UNRESOLVED;
     strncpy(ret->hostname, host, lenof(ret->hostname));
     ret->hostname[lenof(ret->hostname)-1] = '\0';
 #ifndef NO_IPV6
@@ -244,18 +278,17 @@ SockAddr sk_nonamelookup(const char *host)
     return ret;
 }
 
-static int sk_nextaddr(SockAddr addr)
+static int sk_nextaddr(SockAddr addr, SockAddrStep *step)
 {
 #ifndef NO_IPV6
-    if (addr->ai && addr->ai->ai_next) {
-       addr->ai = addr->ai->ai_next;
-       addr->family = addr->ai->ai_family;
+    if (step->ai && step->ai->ai_next) {
+       step->ai = step->ai->ai_next;
        return TRUE;
     } else
        return FALSE;
 #else
-    if (addr->curraddr+1 < addr->naddresses) {
-       addr->curraddr++;
+    if (step->curraddr+1 < addr->naddresses) {
+       step->curraddr++;
        return TRUE;
     } else {
        return FALSE;
@@ -266,20 +299,22 @@ static int sk_nextaddr(SockAddr addr)
 void sk_getaddr(SockAddr addr, char *buf, int buflen)
 {
 
-    if (addr->family == AF_UNSPEC) {
+    if (addr->superfamily == UNRESOLVED) {
        strncpy(buf, addr->hostname, buflen);
        buf[buflen-1] = '\0';
     } else {
 #ifndef NO_IPV6
-       if (getnameinfo(addr->ai->ai_addr, addr->ai->ai_addrlen, buf, buflen,
+       if (getnameinfo(addr->ais->ai_addr, addr->ais->ai_addrlen, buf, buflen,
                        NULL, 0, NI_NUMERICHOST) != 0) {
            buf[0] = '\0';
            strncat(buf, "<unknown>", buflen - 1);
        }
 #else
        struct in_addr a;
-       assert(addr->family == AF_INET);
-       a.s_addr = htonl(addr->addresses[addr->curraddr]);
+       SockAddrStep step;
+       START_STEP(addr, step);
+       assert(SOCKADDR_FAMILY(addr, step) == AF_INET);
+       a.s_addr = htonl(addr->addresses[0]);
        strncpy(buf, inet_ntoa(a), buflen);
        buf[buflen-1] = '\0';
 #endif
@@ -320,15 +355,17 @@ static int sockaddr_is_loopback(struct sockaddr *sa)
 int sk_address_is_local(SockAddr addr)
 {
 
-    if (addr->family == AF_UNSPEC)
+    if (addr->superfamily == UNRESOLVED)
        return 0;                      /* we don't know; assume not */
     else {
 #ifndef NO_IPV6
-       return sockaddr_is_loopback(addr->ai->ai_addr);
+       return sockaddr_is_loopback(addr->ais->ai_addr);
 #else
        struct in_addr a;
-       assert(addr->family == AF_INET);
-       a.s_addr = htonl(addr->addresses[addr->curraddr]);
+       SockAddrStep step;
+       START_STEP(addr, step);
+       assert(SOCKADDR_FAMILY(addr, step) == AF_INET);
+       a.s_addr = htonl(addr->addresses[0]);
        return ipv4_is_loopback(a);
 #endif
     }
@@ -336,30 +373,39 @@ int sk_address_is_local(SockAddr addr)
 
 int sk_addrtype(SockAddr addr)
 {
-    return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    return (family == AF_INET ? ADDRTYPE_IPV4 :
 #ifndef NO_IPV6
-           addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
+           family == AF_INET6 ? ADDRTYPE_IPV6 :
 #endif
            ADDRTYPE_NAME);
 }
 
 void sk_addrcopy(SockAddr addr, char *buf)
 {
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
 
 #ifndef NO_IPV6
-    if (addr->family == AF_INET)
-       memcpy(buf, &((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr,
+    if (family == AF_INET)
+       memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,
               sizeof(struct in_addr));
-    else if (addr->family == AF_INET6)
-       memcpy(buf, &((struct sockaddr_in6 *)addr->ai->ai_addr)->sin6_addr,
+    else if (family == AF_INET6)
+       memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,
               sizeof(struct in6_addr));
     else
        assert(FALSE);
 #else
     struct in_addr a;
 
-    assert(addr->family == AF_INET);
-    a.s_addr = htonl(addr->addresses[addr->curraddr]);
+    assert(family == AF_INET);
+    a.s_addr = htonl(addr->addresses[step.curraddr]);
     memcpy(buf, (char*) &a.s_addr, 4);
 #endif
 }
@@ -463,7 +509,7 @@ static int try_connect(Actual_Socket sock)
     const struct sockaddr *sa;
     int err = 0;
     short localport;
-    int fl, salen;
+    int fl, salen, family;
 
     /*
      * Remove the socket from the tree before we overwrite its
@@ -481,8 +527,9 @@ static int try_connect(Actual_Socket sock)
     /*
      * Open socket.
      */
-    assert(sock->addr->family != AF_UNSPEC);
-    s = socket(sock->addr->family, SOCK_STREAM, 0);
+    family = SOCKADDR_FAMILY(sock->addr, sock->step);
+    assert(family != AF_UNSPEC);
+    s = socket(family, SOCK_STREAM, 0);
     sock->s = s;
 
     if (s < 0) {
@@ -523,13 +570,13 @@ static int try_connect(Actual_Socket sock)
 
     /* We don't try to bind to a local address for UNIX domain sockets.  (Why
      * do we bother doing the bind when localport == 0 anyway?) */
-    if(sock->addr->family != AF_UNIX) {
+    if (family != AF_UNIX) {
        /* Loop round trying to bind */
        while (1) {
            int retcode;
 
 #ifndef NO_IPV6
-           if (sock->addr->family == AF_INET6) {
+           if (family == AF_INET6) {
                /* XXX use getaddrinfo to get a local address? */
                a6.sin6_family = AF_INET6;
                a6.sin6_addr = in6addr_any;
@@ -538,7 +585,7 @@ static int try_connect(Actual_Socket sock)
            } else
 #endif
            {
-               assert(sock->addr->family == AF_INET);
+               assert(family == AF_INET);
                a.sin_family = AF_INET;
                a.sin_addr.s_addr = htonl(INADDR_ANY);
                a.sin_port = htons(localport);
@@ -567,25 +614,25 @@ static int try_connect(Actual_Socket sock)
     /*
      * Connect to remote address.
      */
-    switch(sock->addr->family) {
+    switch(family) {
 #ifndef NO_IPV6
       case AF_INET:
        /* XXX would be better to have got getaddrinfo() to fill in the port. */
-       ((struct sockaddr_in *)sock->addr->ai->ai_addr)->sin_port =
+       ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =
            htons(sock->port);
-       sa = (const struct sockaddr *)sock->addr->ai->ai_addr;
-       salen = sock->addr->ai->ai_addrlen;
+       sa = (const struct sockaddr *)sock->step.ai->ai_addr;
+       salen = sock->step.ai->ai_addrlen;
        break;
       case AF_INET6:
-       ((struct sockaddr_in *)sock->addr->ai->ai_addr)->sin_port =
+       ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =
            htons(sock->port);
-       sa = (const struct sockaddr *)sock->addr->ai->ai_addr;
-       salen = sock->addr->ai->ai_addrlen;
+       sa = (const struct sockaddr *)sock->step.ai->ai_addr;
+       salen = sock->step.ai->ai_addrlen;
        break;
 #else
       case AF_INET:
        a.sin_family = AF_INET;
-       a.sin_addr.s_addr = htonl(sock->addr->addresses[sock->addr->curraddr]);
+       a.sin_addr.s_addr = htonl(sock->addr->addresses[sock->step.curraddr]);
        a.sin_port = htons((short) sock->port);
        sa = (const struct sockaddr *)&a;
        salen = sizeof a;
@@ -663,6 +710,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->oobpending = FALSE;
     ret->listener = 0;
     ret->addr = addr;
+    START_STEP(ret->addr, ret->step);
     ret->s = -1;
     ret->oobinline = oobinline;
     ret->nodelay = nodelay;
@@ -673,7 +721,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     err = 0;
     do {
         err = try_connect(ret);
-    } while (err && sk_nextaddr(ret->addr));
+    } while (err && sk_nextaddr(ret->addr, &ret->step));
 
     if (err)
         ret->error = strerror(err);
@@ -1179,7 +1227,7 @@ static int net_select_result(int fd, int event)
             int err = errno;
            if (s->addr) {
                plug_log(s->plug, 1, s->addr, s->port, strerror(err), err);
-               while (s->addr && sk_nextaddr(s->addr)) {
+               while (s->addr && sk_nextaddr(s->addr, &s->step)) {
                    err = try_connect(s);
                }
            }
@@ -1339,7 +1387,7 @@ SockAddr platform_get_x11_unix_address(const char *display, int displaynum,
     int n;
 
     memset(ret, 0, sizeof *ret);
-    ret->family = AF_UNIX;
+    ret->superfamily = UNIX;
     /*
      * Mac OS X Leopard uses an innovative X display naming
      * convention in which the entire display name is the path to
@@ -1362,10 +1410,10 @@ SockAddr platform_get_x11_unix_address(const char *display, int displaynum,
     else
        *canonicalname = dupstr(ret->hostname);
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #else
     ret->addresses = NULL;
-    ret->curraddr = ret->naddresses = 0;
+    ret->naddresses = 0;
 #endif
     return ret;
 }
index 89d2f47..93eb40c 100644 (file)
@@ -34,6 +34,19 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
  */
 typedef struct Socket_tag *Actual_Socket;
 
+/*
+ * Mutable state that goes with a SockAddr: stores information
+ * about where in the list of candidate IP(v*) addresses we've
+ * currently got to.
+ */
+typedef struct SockAddrStep_tag SockAddrStep;
+struct SockAddrStep_tag {
+#ifndef NO_IPV6
+    struct addrinfo *ai;              /* steps along addr->ais */
+#endif
+    int curraddr;
+};
+
 struct Socket_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -52,6 +65,7 @@ struct Socket_tag {
     int sending_oob;
     int oobinline, nodelay, keepalive, privport;
     SockAddr addr;
+    SockAddrStep step;
     int port;
     int pending_error;                /* in case send() returns error */
     /*
@@ -65,27 +79,42 @@ struct Socket_tag {
 
 struct SockAddr_tag {
     char *error;
-    /* 
-     * Which address family this address belongs to. AF_INET for
-     * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name
-     * resolution has not been done and a simple host name is held
-     * in this SockAddr structure.
-     * The hostname field is also used when the hostname has both
-     * an IPv6 and IPv4 address and the IPv6 connection attempt
-     * fails. We then try the IPv4 address.
-     * This 'family' should become an option in the GUI and
-     * on the commandline for selecting a default protocol.
-     */
-    int family;
+    int resolved;
 #ifndef NO_IPV6
     struct addrinfo *ais;             /* Addresses IPv6 style. */
-    struct addrinfo *ai;              /* steps along the linked list */
 #endif
     unsigned long *addresses;         /* Addresses IPv4 style. */
-    int naddresses, curraddr;
+    int naddresses;
     char hostname[512];                       /* Store an unresolved host name. */
 };
 
+/*
+ * Which address family this address belongs to. AF_INET for IPv4;
+ * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has
+ * not been done and a simple host name is held in this SockAddr
+ * structure.
+ */
+#ifndef NO_IPV6
+#define SOCKADDR_FAMILY(addr, step) \
+    (!(addr)->resolved ? AF_UNSPEC : \
+     (step).ai ? (step).ai->ai_family : AF_INET)
+#else
+#define SOCKADDR_FAMILY(addr, step) \
+    (!(addr)->resolved ? AF_UNSPEC : AF_INET)
+#endif
+
+/*
+ * Start a SockAddrStep structure to step through multiple
+ * addresses.
+ */
+#ifndef NO_IPV6
+#define START_STEP(addr, step) \
+    ((step).ai = (addr)->ais, (step).curraddr = 0)
+#else
+#define START_STEP(addr, step) \
+    ((step).curraddr = 0)
+#endif
+
 static tree234 *sktree;
 
 static int cmpfortree(void *av, void *bv)
@@ -398,21 +427,23 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
     unsigned long a;
     struct hostent *h = NULL;
     char realhost[8192];
-    int ret_family;
+    int hint_family;
     int err;
 
-    /* Clear the structure and default to IPv4. */
-    memset(ret, 0, sizeof(struct SockAddr_tag));
-    ret->family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+    /* Default to IPv4. */
+    hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
 #ifndef NO_IPV6
                   address_family == ADDRTYPE_IPV6 ? AF_INET6 :
 #endif
                   AF_UNSPEC);
+
+    /* Clear the structure and default to IPv4. */
+    memset(ret, 0, sizeof(struct SockAddr_tag));
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #endif
     ret->addresses = NULL;
-    ret_family = AF_UNSPEC;
+    ret->resolved = FALSE;
     *realhost = '\0';
 
     if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
@@ -426,11 +457,10 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
            logevent(NULL, "Using getaddrinfo() for resolving");
 #endif
            memset(&hints, 0, sizeof(hints));
-           hints.ai_family = ret->family;
+           hints.ai_family = hint_family;
            hints.ai_flags = AI_CANONNAME;
            if ((err = p_getaddrinfo(host, NULL, &hints, &ret->ais)) == 0)
-               ret_family = ret->ais->ai_family;
-           ret->ai = ret->ais;
+               ret->resolved = TRUE;
        } else
 #endif
        {
@@ -442,12 +472,12 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
             * (NOTE: we don't use gethostbyname as a fallback!)
             */
            if ( (h = p_gethostbyname(host)) )
-               ret_family = AF_INET;
+               ret->resolved = TRUE;
            else
                err = p_WSAGetLastError();
        }
 
-       if (ret_family == AF_UNSPEC) {
+       if (!ret->resolved) {
            ret->error = (err == WSAENETDOWN ? "Network is down" :
                          err == WSAHOST_NOT_FOUND ? "Host does not exist" :
                          err == WSATRY_AGAIN ? "Host not found" :
@@ -457,20 +487,19 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
                          "gethostbyname: unknown error");
        } else {
            ret->error = NULL;
-           ret->family = ret_family;
 
 #ifndef NO_IPV6
            /* If we got an address info use that... */
-           if (ret->ai) {
+           if (ret->ais) {
                /* Are we in IPv4 fallback mode? */
                /* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */
-               if (ret->family == AF_INET)
+               if (ret->ais->ai_family == AF_INET)
                    memcpy(&a,
-                          (char *) &((SOCKADDR_IN *) ret->ai->
+                          (char *) &((SOCKADDR_IN *) ret->ais->
                                      ai_addr)->sin_addr, sizeof(a));
 
-               if (ret->ai->ai_canonname)
-                   strncpy(realhost, ret->ai->ai_canonname, lenof(realhost));
+               if (ret->ais->ai_canonname)
+                   strncpy(realhost, ret->ais->ai_canonname, lenof(realhost));
                else
                    strncpy(realhost, host, lenof(realhost));
            }
@@ -486,7 +515,6 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
                    memcpy(&a, h->h_addr_list[n], sizeof(a));
                    ret->addresses[n] = p_ntohl(a);
                }
-               ret->curraddr = 0;
                memcpy(&a, h->h_addr, sizeof(a));
                /* This way we are always sure the h->h_name is valid :) */
                strncpy(realhost, h->h_name, sizeof(realhost));
@@ -499,9 +527,8 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
         */
        ret->addresses = snewn(1, unsigned long);
        ret->naddresses = 1;
-       ret->curraddr = 0;
        ret->addresses[0] = p_ntohl(a);
-       ret->family = AF_INET;
+       ret->resolved = TRUE;
        strncpy(realhost, host, sizeof(realhost));
     }
     realhost[lenof(realhost)-1] = '\0';
@@ -514,9 +541,9 @@ SockAddr sk_nonamelookup(const char *host)
 {
     SockAddr ret = snew(struct SockAddr_tag);
     ret->error = NULL;
-    ret->family = AF_UNSPEC;
+    ret->resolved = FALSE;
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #endif
     ret->addresses = NULL;
     ret->naddresses = 0;
@@ -525,20 +552,19 @@ SockAddr sk_nonamelookup(const char *host)
     return ret;
 }
 
-int sk_nextaddr(SockAddr addr)
+int sk_nextaddr(SockAddr addr, SockAddrStep *step)
 {
 #ifndef NO_IPV6
-    if (addr->ai) {
-       if (addr->ai->ai_next) {
-           addr->ai = addr->ai->ai_next;
-           addr->family = addr->ai->ai_family;
+    if (step->ai) {
+       if (step->ai->ai_next) {
+           step->ai = step->ai->ai_next;
            return TRUE;
        } else
            return FALSE;
     }
 #endif
-    if (addr->curraddr+1 < addr->naddresses) {
-       addr->curraddr++;
+    if (step->curraddr+1 < addr->naddresses) {
+       step->curraddr++;
        return TRUE;
     } else {
        return FALSE;
@@ -547,19 +573,22 @@ int sk_nextaddr(SockAddr addr)
 
 void sk_getaddr(SockAddr addr, char *buf, int buflen)
 {
+    SockAddrStep step;
+    START_STEP(addr, step);
+
 #ifndef NO_IPV6
-    if (addr->ai) {
+    if (step.ai) {
        if (p_WSAAddressToStringA) {
-           p_WSAAddressToStringA(addr->ai->ai_addr, addr->ai->ai_addrlen,
+           p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,
                                  NULL, buf, &buflen);
        } else
            strncpy(buf, "IPv6", buflen);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (SOCKADDR_FAMILY(addr, step) == AF_INET) {
        struct in_addr a;
-       assert(addr->addresses && addr->curraddr < addr->naddresses);
-       a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+       assert(addr->addresses && step.curraddr < addr->naddresses);
+       a.s_addr = p_htonl(addr->addresses[step.curraddr]);
        strncpy(buf, p_inet_ntoa(a), buflen);
        buf[buflen-1] = '\0';
     } else {
@@ -606,58 +635,73 @@ static int ipv4_is_local_addr(struct in_addr addr)
 
 int sk_address_is_local(SockAddr addr)
 {
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
 #ifndef NO_IPV6
-    if (addr->family == AF_INET6) {
-       return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)addr->ai->ai_addr);
+    if (family == AF_INET6) {
+       return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)step.ai->ai_addr);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (family == AF_INET) {
 #ifndef NO_IPV6
-       if (addr->ai) {
-           return ipv4_is_local_addr(((struct sockaddr_in *)addr->ai->ai_addr)
+       if (step.ai) {
+           return ipv4_is_local_addr(((struct sockaddr_in *)step.ai->ai_addr)
                                      ->sin_addr);
        } else
 #endif
        {
            struct in_addr a;
-           assert(addr->addresses && addr->curraddr < addr->naddresses);
-           a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+           assert(addr->addresses && step.curraddr < addr->naddresses);
+           a.s_addr = p_htonl(addr->addresses[step.curraddr]);
            return ipv4_is_local_addr(a);
        }
     } else {
-       assert(addr->family == AF_UNSPEC);
+       assert(family == AF_UNSPEC);
        return 0;                      /* we don't know; assume not */
     }
 }
 
 int sk_addrtype(SockAddr addr)
 {
-    return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    return (family == AF_INET ? ADDRTYPE_IPV4 :
 #ifndef NO_IPV6
-           addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
+           family == AF_INET6 ? ADDRTYPE_IPV6 :
 #endif
            ADDRTYPE_NAME);
 }
 
 void sk_addrcopy(SockAddr addr, char *buf)
 {
-    assert(addr->family != AF_UNSPEC);
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    assert(family != AF_UNSPEC);
 #ifndef NO_IPV6
-    if (addr->ai) {
-       if (addr->family == AF_INET)
-           memcpy(buf, &((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr,
+    if (step.ai) {
+       if (family == AF_INET)
+           memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,
                   sizeof(struct in_addr));
-       else if (addr->family == AF_INET6)
-           memcpy(buf, &((struct sockaddr_in6 *)addr->ai->ai_addr)->sin6_addr,
+       else if (family == AF_INET6)
+           memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,
                   sizeof(struct in6_addr));
        else
            assert(FALSE);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (family == AF_INET) {
        struct in_addr a;
-       assert(addr->addresses && addr->curraddr < addr->naddresses);
-       a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+       assert(addr->addresses && step.curraddr < addr->naddresses);
+       a.s_addr = p_htonl(addr->addresses[step.curraddr]);
        memcpy(buf, (char*) &a.s_addr, 4);
     }
 }
@@ -780,17 +824,7 @@ static DWORD try_connect(Actual_Socket sock)
     /*
      * Open socket.
      */
-#ifndef NO_IPV6
-    /* Let's default to IPv6, this shouldn't hurt anybody
-     * If the stack supports IPv6 it will also allow IPv4 connections. */
-    if (sock->addr->ai) {
-       family = sock->addr->ai->ai_family;
-    } else
-#endif
-    {
-       /* Default to IPv4 */
-       family = AF_INET;
-    }
+    family = SOCKADDR_FAMILY(sock->addr, sock->step);
 
     /*
      * Remove the socket from the tree before we overwrite its
@@ -850,11 +884,10 @@ static DWORD try_connect(Actual_Socket sock)
            a.sin_port = p_htons(localport);
        }
 #ifndef NO_IPV6
-       sockcode = p_bind(s, (sock->addr->family == AF_INET6 ?
-                          (struct sockaddr *) &a6 :
-                          (struct sockaddr *) &a),
-                      (sock->addr->family ==
-                       AF_INET6 ? sizeof(a6) : sizeof(a)));
+       sockcode = p_bind(s, (family == AF_INET6 ?
+                             (struct sockaddr *) &a6 :
+                             (struct sockaddr *) &a),
+                         (family == AF_INET6 ? sizeof(a6) : sizeof(a)));
 #else
        sockcode = p_bind(s, (struct sockaddr *) &a, sizeof(a));
 #endif
@@ -883,26 +916,26 @@ static DWORD try_connect(Actual_Socket sock)
      * Connect to remote address.
      */
 #ifndef NO_IPV6
-    if (sock->addr->ai) {
+    if (sock->step.ai) {
        if (family == AF_INET6) {
            a6.sin6_family = AF_INET6;
            a6.sin6_port = p_htons((short) sock->port);
            a6.sin6_addr =
-               ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_addr;
-           a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_flowinfo;
-           a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_scope_id;
+               ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_addr;
+           a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_flowinfo;
+           a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_scope_id;
        } else {
            a.sin_family = AF_INET;
            a.sin_addr =
-               ((struct sockaddr_in *) sock->addr->ai->ai_addr)->sin_addr;
+               ((struct sockaddr_in *) sock->step.ai->ai_addr)->sin_addr;
            a.sin_port = p_htons((short) sock->port);
        }
     } else
 #endif
     {
-       assert(sock->addr->addresses && sock->addr->curraddr < sock->addr->naddresses);
+       assert(sock->addr->addresses && sock->step.curraddr < sock->addr->naddresses);
        a.sin_family = AF_INET;
-       a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->addr->curraddr]);
+       a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->step.curraddr]);
        a.sin_port = p_htons((short) sock->port);
     }
 
@@ -998,12 +1031,13 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->privport = privport;
     ret->port = port;
     ret->addr = addr;
+    START_STEP(ret->addr, ret->step);
     ret->s = INVALID_SOCKET;
 
     err = 0;
     do {
         err = try_connect(ret);
-    } while (err && sk_nextaddr(ret->addr));
+    } while (err && sk_nextaddr(ret->addr, &ret->step));
 
     return (Socket) ret;
 }
@@ -1349,7 +1383,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
        if (s->addr) {
            plug_log(s->plug, 1, s->addr, s->port,
                     winsock_error_string(err), err);
-           while (s->addr && sk_nextaddr(s->addr)) {
+           while (s->addr && sk_nextaddr(s->addr, &s->step)) {
                err = try_connect(s);
            }
        }