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
\b for a remote-to-local port forwarding, PuTTY will choose a
sensible protocol for the outgoing connection.
\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
\H{config-ssh-bugs} The Bugs panel
#define ipv4_is_loopback(addr) \
((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L)
#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 */
struct Socket_tag {
const struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
int sending_oob;
int oobinline;
int pending_error; /* in case send() returns error */
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;
/*
struct SockAddr_tag {
char *error;
/*
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
ret->pending_error = 0;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
ret->pending_error = 0;
+ ret->parent = ret->child = NULL;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
ret->pending_error = 0;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
ret->pending_error = 0;
+ ret->parent = ret->child = NULL;
}
Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
}
Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
+ int orig_address_family)
{
static const struct socket_function_table fn_table = {
sk_tcp_plug,
{
static const struct socket_function_table fn_table = {
sk_tcp_plug,
/*
* Create Socket structure.
*/
/*
* Create Socket structure.
*/
ret->frozen_readable = 0;
ret->localhost_only = local_host_only;
ret->pending_error = 0;
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.
*/
/*
* 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 :
- address_family == ADDRTYPE_IPV6 ? AF_INET6 :
+ orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :
-
-#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;
ret->error = winsock_error_string(err);
return (Socket) ret;
}
ret->error = winsock_error_string(err);
return (Socket) ret;
}
* window, or an EventSelect on an event object. */
errstr = do_select(s, 1);
if (errstr) {
* window, or an EventSelect on an event object. */
errstr = do_select(s, 1);
if (errstr) {
ret->error = errstr;
return (Socket) ret;
}
add234(sktree, ret);
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
+
extern char *do_select(SOCKET skt, int startup);
Actual_Socket s = (Actual_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);
del234(sktree, s);
do_select(s->s, 0);
p_closesocket(s->s);