For local and dynamic port forwardings (i.e. the ones which listen
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 8 Jan 2005 14:45:26 +0000 (14:45 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 8 Jan 2005 14:45:26 +0000 (14:45 +0000)
on a local port), the `Auto' protocol option on the Tunnels panel
should always produce a port you can connect to in _either_ of IPv4
and v6, because the aim is for the user not to have to know or care
which one they're using. This was not the case on Windows, and now
is. Also, updated the docs to give more detail on issues like this.

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

doc/config.but
windows/winnet.c

index 4a2d382..c59881e 100644 (file)
@@ -2494,12 +2494,15 @@ incoming connections in both IPv4 and (if available) IPv6
 \b for a remote-to-local port forwarding, PuTTY will choose a
 sensible protocol for the outgoing connection.
 
-\# FIXME: work out what this paragraph means, reword it for clarity,
-\# and reinstate it.
-Note that on Windows the address space for IPv4 and IPv6 is
-completely disjunct, so listening on IPv6 won't make PuTTY listen on
-IPv4. This behaviour may be different on most remote hosts when they
-are not operating Windows.
+Note that some operating systems may listen for incoming connections
+in IPv4 even if you specifically asked for IPv6, because their IPv4
+and IPv6 protocol stacks are linked together. Apparently Linux does
+this, and Windows does not. So if you're running PuTTY on Windows
+and you tick \q{IPv6} for a local or dynamic port forwarding, it
+will \e{only} be usable by connecting to it using IPv6; whereas if
+you do the same on Linux, you can also use it with IPv4. However,
+ticking \q{Auto} should always give you a port which you can connect
+to using either protocol.
 
 \H{config-ssh-bugs} The Bugs panel
 
index 4930698..eccdb6f 100644 (file)
@@ -24,6 +24,16 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
 #define ipv4_is_loopback(addr) \
        ((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L)
 
+/*
+ * We used to typedef struct Socket_tag *Socket.
+ *
+ * Since we have made the networking abstraction slightly more
+ * abstract, Socket no longer means a tcp socket (it could mean
+ * an ssl socket).  So now we must use Actual_Socket when we know
+ * we are talking about a tcp socket.
+ */
+typedef struct Socket_tag *Actual_Socket;
+
 struct Socket_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -42,18 +52,15 @@ struct Socket_tag {
     int sending_oob;
     int oobinline;
     int pending_error;                /* in case send() returns error */
+    /*
+     * We sometimes need pairs of Socket structures to be linked:
+     * if we are listening on the same IPv6 and v4 port, for
+     * example. So here we define `parent' and `child' pointers to
+     * track this link.
+     */
+    Actual_Socket parent, child;
 };
 
-/*
- * We used to typedef struct Socket_tag *Socket.
- *
- * Since we have made the networking abstraction slightly more
- * abstract, Socket no longer means a tcp socket (it could mean
- * an ssl socket).  So now we must use Actual_Socket when we know
- * we are talking about a tcp socket.
- */
-typedef struct Socket_tag *Actual_Socket;
-
 struct SockAddr_tag {
     char *error;
     /* 
@@ -618,6 +625,7 @@ Socket sk_register(void *sock, Plug plug)
     ret->frozen_readable = 0;
     ret->localhost_only = 0;          /* unused, but best init anyway */
     ret->pending_error = 0;
+    ret->parent = ret->child = NULL;
 
     ret->s = (SOCKET)sock;
 
@@ -682,6 +690,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->frozen_readable = 0;
     ret->localhost_only = 0;          /* unused, but best init anyway */
     ret->pending_error = 0;
+    ret->parent = ret->child = NULL;
 
     /*
      * Open socket.
@@ -837,7 +846,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 }
 
 Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
-                     int address_family)
+                     int orig_address_family)
 {
     static const struct socket_function_table fn_table = {
        sk_tcp_plug,
@@ -863,6 +872,8 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
     int retcode;
     int on = 1;
 
+    int address_family;
+
     /*
      * Create Socket structure.
      */
@@ -877,25 +888,26 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
     ret->frozen_readable = 0;
     ret->localhost_only = local_host_only;
     ret->pending_error = 0;
+    ret->parent = ret->child = NULL;
 
     /*
      * Translate address_family from platform-independent constants
      * into local reality.
      */
-    address_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+    address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET :
 #ifndef NO_IPV6
-                     address_family == ADDRTYPE_IPV6 ? AF_INET6 :
+                     orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :
 #endif
                      AF_UNSPEC);
-#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 (address_family == AF_UNSPEC) address_family = AF_INET6;
-#else
-    /* No other choice, default to IPv4 */
-    if (address_family == AF_UNSPEC)  address_family = AF_INET;
-#endif
+
+    /*
+     * Our default, if passed the `don't care' value
+     * ADDRTYPE_UNSPEC, is to listen on IPv4. If IPv6 is supported,
+     * we will also set up a second socket listening on IPv6, but
+     * the v4 one is primary since that ought to work even on
+     * non-v6-supporting systems.
+     */
+    if (address_family == AF_UNSPEC) address_family = AF_INET;
 
     /*
      * Open socket.
@@ -974,6 +986,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
        }
 
     if (err) {
+       p_closesocket(s);
        ret->error = winsock_error_string(err);
        return (Socket) ret;
     }
@@ -989,12 +1002,35 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
      * window, or an EventSelect on an event object. */
     errstr = do_select(s, 1);
     if (errstr) {
+       p_closesocket(s);
        ret->error = errstr;
        return (Socket) ret;
     }
 
     add234(sktree, ret);
 
+#ifndef NO_IPV6
+    /*
+     * If we were given ADDRTYPE_UNSPEC, we must also create an
+     * IPv6 listening socket and link it to this one.
+     */
+    if (address_family == AF_INET && orig_address_family == ADDRTYPE_UNSPEC) {
+       Actual_Socket other;
+
+       other = (Actual_Socket) sk_newlistener(srcaddr, port, plug,
+                                              local_host_only, ADDRTYPE_IPV6);
+
+       if (other) {
+           if (!other->error) {
+               other->parent = ret;
+               ret->child = other;
+           } else {
+               sfree(other);
+           }
+       }
+    }
+#endif
+
     return (Socket) ret;
 }
 
@@ -1003,6 +1039,9 @@ static void sk_tcp_close(Socket sock)
     extern char *do_select(SOCKET skt, int startup);
     Actual_Socket s = (Actual_Socket) sock;
 
+    if (s->child)
+       sk_tcp_close((Socket)s->child);
+
     del234(sktree, s);
     do_select(s->s, 0);
     p_closesocket(s->s);