# build, since at the time of writing this <multimon.h> is
# known not to be available in Cygwin.
#
+# - COMPAT=/DNO_IPV6
+# Disables PuTTY's ability to make IPv6 connections, enabling
+# it to compile under development environments which do not
+# support IPv6 in their header files.
+#
# - COMPAT=/DMSVC4
# - RCFL=/DMSVC4
# Makes a couple of minor changes so that PuTTY compiles using
cfg->keyfile = filename_from_str(value);
}
+ if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {
+ RETURN(1);
+ SAVEABLE(1);
+ cfg->addressfamily = ADDRTYPE_IPV4;
+ }
+ if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {
+ RETURN(1);
+ SAVEABLE(1);
+ cfg->addressfamily = ADDRTYPE_IPV6;
+ }
+
return ret; /* unrecognised */
}
struct portfwd_data {
union control *addbutton, *rembutton, *listbox;
union control *sourcebox, *destbox, *direction;
+ union control *addressfamily;
};
static void portfwd_handler(union control *ctrl, void *dlg,
* Default is Local.
*/
dlg_radiobutton_set(ctrl, dlg, 0);
+ } else if (ctrl == pfd->addressfamily) {
+ dlg_radiobutton_set(ctrl, dlg, 0);
}
} else if (event == EVENT_ACTION) {
if (ctrl == pfd->addbutton) {
char str[sizeof(cfg->portfwd)];
char *p;
- int whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
+ int i, type;
+ int whichbutton;
+
+ i = 0;
+ whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
+ if (whichbutton == 1)
+ str[i++] = '4';
+ else if (whichbutton == 2)
+ str[i++] = '6';
+
+ whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
if (whichbutton == 0)
- str[0] = 'L';
+ type = 'L';
else if (whichbutton == 1)
- str[0] = 'R';
+ type = 'R';
else
- str[0] = 'D';
- dlg_editbox_get(pfd->sourcebox, dlg, str+1, sizeof(str) - 2);
- if (!str[1]) {
+ type = 'D';
+ str[i++] = type;
+
+ dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);
+ if (!str[2]) {
dlg_error_msg(dlg, "You need to specify a source port number");
return;
}
p = str + strlen(str);
- if (str[0] != 'D') {
+ if (type != 'D') {
*p++ = '\t';
dlg_editbox_get(pfd->destbox, dlg, p,
sizeof(str)-1 - (p - str));
if (protocol >= 0) {
ctrl_settitle(b, "Connection", "Options controlling the connection");
+ s = ctrl_getset(b, "Connection", "keepalive",
+ "Sending of null packets to keep session active");
+ ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
+ HELPCTX(connection_keepalive),
+ dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
+ I(-1));
+
if (!midsession) {
- s = ctrl_getset(b, "Connection", "data",
- "Data to send to the server");
+ s = ctrl_getset(b, "Connection", "tcp",
+ "Low-level TCP connection options");
+ ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
+ 'n', HELPCTX(connection_nodelay),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,tcp_nodelay)));
+ ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
+ 'p', HELPCTX(connection_tcpkeepalive),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,tcp_keepalives)));
+ s = ctrl_getset(b, "Connection", "ipversion",
+ "Internet protocol version");
+ ctrl_radiobuttons(s, NULL, NO_SHORTCUT,
+#ifndef NO_IPV6
+ 3,
+#else
+ 2,
+#endif
+ HELPCTX(connection_ipversion),
+ dlg_stdradiobutton_handler,
+ I(offsetof(Config, addressfamily)),
+ "Auto", NO_SHORTCUT, I(ADDRTYPE_UNSPEC),
+ "IPv4", NO_SHORTCUT, I(ADDRTYPE_IPV4),
+#ifndef NO_IPV6
+ "IPv6", NO_SHORTCUT, I(ADDRTYPE_IPV6),
+#endif
+ NULL);
+ }
+
+ /*
+ * A sub-panel Connection/Data, containing options that
+ * decide on data to send to the server.
+ */
+ if (!midsession) {
+ ctrl_settitle(b, "Connection/Data", "Data to send to the server");
+
+ s = ctrl_getset(b, "Connection/Data", "login",
+ "Login details");
+ ctrl_editbox(s, "Auto-login username", 'u', 50,
+ HELPCTX(connection_username),
+ dlg_stdeditbox_handler, I(offsetof(Config,username)),
+ I(sizeof(((Config *)0)->username)));
+
+ s = ctrl_getset(b, "Connection/Data", "term",
+ "Terminal details");
ctrl_editbox(s, "Terminal-type string", 't', 50,
HELPCTX(connection_termtype),
dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
HELPCTX(connection_termspeed),
dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
I(sizeof(((Config *)0)->termspeed)));
- ctrl_editbox(s, "Auto-login username", 'u', 50,
- HELPCTX(connection_username),
- dlg_stdeditbox_handler, I(offsetof(Config,username)),
- I(sizeof(((Config *)0)->username)));
- ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
+ s = ctrl_getset(b, "Connection/Data", "env",
+ "Environment variables");
ctrl_columns(s, 2, 80, 20);
ed = (struct environ_data *)
ctrl_alloc(b, sizeof(struct environ_data));
ed->listbox->listbox.percentages[1] = 70;
}
- s = ctrl_getset(b, "Connection", "keepalive",
- "Sending of null packets to keep session active");
- ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
- HELPCTX(connection_keepalive),
- dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
- I(-1));
-
- if (!midsession) {
- s = ctrl_getset(b, "Connection", "tcp",
- "Low-level TCP connection options");
- ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
- 'n', HELPCTX(connection_nodelay),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,tcp_nodelay)));
- ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
- 'p', HELPCTX(connection_tcpkeepalive),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,tcp_keepalives)));
- }
-
}
if (!midsession) {
dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
}
- /*
- * The Connection/SSH/Tunnels panel. Some of this _is_
- * still available in mid-session.
- */
- ctrl_settitle(b, "Connection/SSH/Tunnels",
- "Options controlling SSH tunnelling");
-
if (!midsession) {
- s = ctrl_getset(b, "Connection/SSH/Tunnels", "x11", "X11 forwarding");
+ /*
+ * The Connection/SSH/X11 panel.
+ */
+ ctrl_settitle(b, "Connection/SSH/X11",
+ "Options controlling SSH X11 forwarding");
+
+ s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
ctrl_checkbox(s, "Enable X11 forwarding", 'e',
HELPCTX(ssh_tunnels_x11),
dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
"XDM-Authorization-1", I(X11_XDM), NULL);
}
+ /*
+ * The Tunnels panel _is_ still available in mid-session.
+ */
+ ctrl_settitle(b, "Connection/SSH/Tunnels",
+ "Options controlling SSH port forwarding");
+
s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
"Port forwarding");
ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
"Remote", 'm', P(NULL),
"Dynamic", 'y', P(NULL),
NULL);
+ pfd->addressfamily =
+ ctrl_radiobuttons(s, NULL, NO_SHORTCUT,
+#ifndef NO_IPV6
+ 3,
+#else
+ 2,
+#endif
+ HELPCTX(ssh_tunnels_portfwd_ipversion),
+ portfwd_handler, P(pfd),
+ "Auto", NO_SHORTCUT, I(ADDRTYPE_UNSPEC),
+ "IPv4", NO_SHORTCUT, I(ADDRTYPE_IPV4),
+#ifndef NO_IPV6
+ "IPv6", NO_SHORTCUT, I(ADDRTYPE_IPV6),
+#endif
+ NULL);
ctrl_tabdelay(s, pfd->addbutton);
ctrl_columns(s, 1, 100);
The Connection panel allows you to configure options that apply to
more than one type of connection.
-\S{config-termtype} \q{Terminal-type string}
-
-\cfg{winhelp-topic}{connection.termtype}
-
-Most servers you might connect to with PuTTY are designed to be
-connected to from lots of different types of terminal. In order to
-send the right control sequences to each one, the server will need
-to know what type of terminal it is dealing with. Therefore, each of
-the SSH, Telnet and Rlogin protocols allow a text string to be sent
-down the connection describing the terminal.
-
-PuTTY attempts to emulate the Unix \c{xterm} program, and by default
-it reflects this by sending \c{xterm} as a terminal-type string. If
-you find this is not doing what you want - perhaps the remote
-system reports \q{Unknown terminal type} - you could try setting
-this to something different, such as \c{vt220}.
-
-If you're not sure whether a problem is due to the terminal type
-setting or not, you probably need to consult the manual for your
-application or your server.
-
-\S{config-termspeed} \q{Terminal speeds}
-
-\cfg{winhelp-topic}{connection.termspeed}
-
-The Telnet, Rlogin, and SSH protocols allow the client to specify
-terminal speeds to the server.
-
-This parameter does \e{not} affect the actual speed of the connection,
-which is always \q{as fast as possible}; it is just a hint that is
-sometimes used by server software to modify its behaviour. For
-instance, if a slow speed is indicated, the server may switch to a
-less bandwidth-hungry display mode.
-
-The value is usually meaningless in a network environment, but
-PuTTY lets you configure it, in case you find the server is reacting
-badly to the default value.
-
-The format is a pair of numbers separated by a comma, for instance,
-\c{38400,38400}. The first number represents the output speed
-(\e{from} the server) in bits per second, and the second is the input
-speed (\e{to} the server). (Only the first is used in the Rlogin
-protocol.)
-
-This option has no effect on Raw connections.
-
-\S{config-username} \q{Auto-login username}
-
-\cfg{winhelp-topic}{connection.username}
-
-All three of the SSH, Telnet and Rlogin protocols allow you to
-specify what user name you want to log in as, without having to type
-it explicitly every time. (Some Telnet servers don't support this.)
-
-In this box you can type that user name.
-
-\S{config-environ} Setting environment variables on the server
-
-\cfg{winhelp-topic}{telnet.environ}
-
-The Telnet protocol provides a means for the client to pass
-environment variables to the server. Many Telnet servers have
-stopped supporting this feature due to security flaws, but PuTTY
-still supports it for the benefit of any servers which have found
-other ways around the security problems than just disabling the
-whole mechanism.
-
-Version 2 of the SSH protocol also provides a similar mechanism,
-which is easier to implement without security flaws. Newer SSH2
-servers are more likely to support it than older ones.
-
-This configuration data is not used in the SSHv1, rlogin or raw
-protocols.
-
-To add an environment variable to the list transmitted down the
-connection, you enter the variable name in the \q{Variable} box,
-enter its value in the \q{Value} box, and press the \q{Add} button.
-To remove one from the list, select it in the list box and press
-\q{Remove}.
-
\S{config-keepalive} Using keepalives to prevent disconnection
\cfg{winhelp-topic}{connection.keepalive}
TCP keepalives are disabled by default.
+\S{config-address-family} \q{Internet protocol}
+
+\cfg{winhelp-topic}{connection.ipversion}
+
+This option allows the user to select between the old and new
+Internet protocols and addressing schemes (IPv4 and IPv6). The
+default setting is \q{Auto}, which means PuTTY will do something
+sensible and try to guess which protocol you wanted. (If you specify
+a literal Internet address, it will use whichever protocol that
+address implies. If you provide a hostname, it will see what kinds
+of address exist for that hostname; it will use IPv6 if there is an
+IPv6 address available, and fall back to IPv4 if not.)
+
+If you need to force PuTTY to use a particular protocol, you can
+explicitly set this to \q{IPv4} or \q{IPv6}.
+
+\H{config-data} The Data panel
+
+The Data panel allows you to configure various pieces of data which
+can be sent to the server to affect your connection at the far end.
+
+Each options on this panel applies to more than one protocol.
+Options which apply to only one protocol appear on that protocol's
+configuration panels.
+
+\S{config-username} \q{Auto-login username}
+
+\cfg{winhelp-topic}{connection.username}
+
+All three of the SSH, Telnet and Rlogin protocols allow you to
+specify what user name you want to log in as, without having to type
+it explicitly every time. (Some Telnet servers don't support this.)
+
+In this box you can type that user name.
+
+\S{config-termtype} \q{Terminal-type string}
+
+\cfg{winhelp-topic}{connection.termtype}
+
+Most servers you might connect to with PuTTY are designed to be
+connected to from lots of different types of terminal. In order to
+send the right control sequences to each one, the server will need
+to know what type of terminal it is dealing with. Therefore, each of
+the SSH, Telnet and Rlogin protocols allow a text string to be sent
+down the connection describing the terminal.
+
+PuTTY attempts to emulate the Unix \c{xterm} program, and by default
+it reflects this by sending \c{xterm} as a terminal-type string. If
+you find this is not doing what you want - perhaps the remote
+system reports \q{Unknown terminal type} - you could try setting
+this to something different, such as \c{vt220}.
+
+If you're not sure whether a problem is due to the terminal type
+setting or not, you probably need to consult the manual for your
+application or your server.
+
+\S{config-termspeed} \q{Terminal speeds}
+
+\cfg{winhelp-topic}{connection.termspeed}
+
+The Telnet, Rlogin, and SSH protocols allow the client to specify
+terminal speeds to the server.
+
+This parameter does \e{not} affect the actual speed of the connection,
+which is always \q{as fast as possible}; it is just a hint that is
+sometimes used by server software to modify its behaviour. For
+instance, if a slow speed is indicated, the server may switch to a
+less bandwidth-hungry display mode.
+
+The value is usually meaningless in a network environment, but
+PuTTY lets you configure it, in case you find the server is reacting
+badly to the default value.
+
+The format is a pair of numbers separated by a comma, for instance,
+\c{38400,38400}. The first number represents the output speed
+(\e{from} the server) in bits per second, and the second is the input
+speed (\e{to} the server). (Only the first is used in the Rlogin
+protocol.)
+
+This option has no effect on Raw connections.
+
+\S{config-environ} Setting environment variables on the server
+
+\cfg{winhelp-topic}{telnet.environ}
+
+The Telnet protocol provides a means for the client to pass
+environment variables to the server. Many Telnet servers have
+stopped supporting this feature due to security flaws, but PuTTY
+still supports it for the benefit of any servers which have found
+other ways around the security problems than just disabling the
+whole mechanism.
+
+Version 2 of the SSH protocol also provides a similar mechanism,
+which is easier to implement without security flaws. Newer SSH2
+servers are more likely to support it than older ones.
+
+This configuration data is not used in the SSHv1, rlogin or raw
+protocols.
+
+To add an environment variable to the list transmitted down the
+connection, you enter the variable name in the \q{Variable} box,
+enter its value in the \q{Value} box, and press the \q{Add} button.
+To remove one from the list, select it in the list box and press
+\q{Remove}.
+
\H{config-proxy} The Proxy panel
\cfg{winhelp-topic}{proxy.main}
This key must be in PuTTY's native format (\c{*.PPK}).
-\H{config-ssh-tunnels} The Tunnels panel
-
-The Tunnels panel allows you to configure tunnelling of other
-connection types through an SSH connection.
-
-\S{config-ssh-x11} X11 forwarding
+\H{config-ssh-x11} The X11 panel
\cfg{winhelp-topic}{ssh.tunnels.x11}
+The X11 panel allows you to configure forwarding of X11 over an
+SSH connection.
+
If your server lets you run X Window System applications, X11
forwarding allows you to securely give those applications access to
a local X display on your PC.
See \k{using-x-forwarding} for more information about X11
forwarding.
-\S2{config-ssh-x11auth} Remote X11 authentication
+\S{config-ssh-x11auth} Remote X11 authentication
\cfg{winhelp-topic}{ssh.tunnels.x11auth}
PuTTY's default is \cw{MIT-MAGIC-COOKIE-1}. If you change it, you
should be sure you know what you're doing.
-\S{config-ssh-portfwd} Port forwarding
+\H{config-ssh-portfwd} The Tunnels panel
\cfg{winhelp-topic}{ssh.tunnels.portfwd}
+The Tunnels panel allows you to configure tunnelling of arbitrary
+connection types through an SSH connection.
+
Port forwarding allows you to tunnel other types of network
connection down an SSH session. See \k{using-port-forwarding} for a
general discussion of port forwarding and how it works.
this feature is only available in the SSH 2 protocol, and not all
SSH 2 servers support it (OpenSSH 3.0 does not, for example).
+\S{config-ssh-portfwd-address-family} Selecting Internet protocol
+version for forwarded ports
+
+\cfg{winhelp-topic}{ssh.tunnels.portfwd.ipversion}
+
+This switch allows you to select a specific Internet protocol (IPv4
+or IPv6) for the local end of a forwarded port. By default, it is
+set on \q{Auto}, which means that:
+
+\b for a local-to-remote port forwarding, PuTTY will listen for
+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.
+
\H{config-ssh-bugs} The Bugs panel
Not all SSH servers work properly. Various existing servers have
\c -A -a enable / disable agent forwarding
\c -t -T enable / disable pty allocation
\c -1 -2 force use of particular protocol version
+\c -4 -6 force use of IPv4 or IPv6
\c -C enable compression
\c -i key private key file for authentication
\c -s remote command is an SSH subsystem (SSH-2 only)
\c -l user connect with specified username
\c -pw passw login with specified password
\c -1 -2 force use of particular SSH protocol version
+\c -4 -6 force use of IPv4 or IPv6
\c -C enable compression
\c -i key private key file for authentication
\c -batch disable all interactive prompts
protocol version as \q{1 only} or \q{2 only} in the SSH panel of the
PuTTY configuration box (see \k{config-ssh-prot}).
+\S2{using-cmdline-ipversion} \i\c{-4} and \i\c{-6}: specify an
+\i{Internet protocol version}
+
+The \c{-4} and \c{-6} options force PuTTY to use the older Internet
+protocol \i{IPv4} or the newer \i{IPv6}.
+
+These options are equivalent to selecting your preferred Internet
+protocol version as \q{IPv4} or \q{IPv6} in the Connection panel of
+the PuTTY configuration box (see \k{config-address-family}).
+
\S2{using-cmdline-identity} \i\c{-i}: specify an SSH \i{private key}
The \c{-i} option allows you to specify the name of a private key
* Network functions exported to the world. These choose whether to call
* MacTCP or OpenTransport and behave accordingly.
*/
-SockAddr sk_namelookup(char const *host, char **canonicalname)
+SockAddr sk_namelookup(char const *host, char **canonicalname, int address_family)
{
if (stack != NULL)
return 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 address_family)
{
if (stack != NULL)
int oobinline, int nodelay, int keepalive,
Plug plug, const Config *cfg);
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
- const Config *cfg);
+ const Config *cfg, int addressfamily);
SockAddr name_lookup(char *host, int port, char **canonicalname,
- const Config *cfg);
+ const Config *cfg, int addressfamily);
/* platform-dependent callback from new_connection() */
/* (same caveat about addr as new_connection()) */
void sk_init(void); /* called once at program startup */
void sk_cleanup(void); /* called just before program exit */
-SockAddr sk_namelookup(const char *host, char **canonicalname);
+SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family);
SockAddr sk_nonamelookup(const char *host);
void sk_getaddr(SockAddr addr, char *buf, int buflen);
int sk_hostname_is_local(char *name);
int sk_address_is_local(SockAddr addr);
-enum { ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME };
int sk_addrtype(SockAddr addr);
void sk_addrcopy(SockAddr addr, char *buf);
void sk_addr_free(SockAddr addr);
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, int keepalive, Plug p);
-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 address_family);
Socket sk_register(OSSocket sock, Plug plug);
* Called when receiving a PORT OPEN from the server
*/
const char *pfd_newconnect(Socket *s, char *hostname, int port,
- void *c, const Config *cfg)
+ void *c, const Config *cfg, int addressfamily)
{
static const struct plug_function_table fn_table = {
pfd_closing,
/*
* Try to find host.
*/
- addr = name_lookup(hostname, port, &dummy_realhost, cfg);
+ addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
*/
const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
int port, void *backhandle, const Config *cfg,
- void **sockdata)
+ void **sockdata, int address_family)
{
static const struct plug_function_table fn_table = {
pfd_closing,
pr->backhandle = backhandle;
pr->s = s = new_listener(srcaddr, port, (Plug) pr,
- !cfg->lport_acceptall, cfg);
+ !cfg->lport_acceptall, cfg, address_family);
if ((err = sk_socket_error(s)) != NULL) {
sfree(pr);
return err;
}
SockAddr name_lookup(char *host, int port, char **canonicalname,
- const Config *cfg)
+ const Config *cfg, int addressfamily)
{
if (cfg->proxy_type != PROXY_NONE &&
do_proxy_dns(cfg) &&
return sk_nonamelookup(host);
}
- return sk_namelookup(host, canonicalname);
+ return sk_namelookup(host, canonicalname, addressfamily);
}
Socket new_connection(SockAddr addr, char *hostname,
/* look-up proxy */
proxy_addr = sk_namelookup(cfg->proxy_host,
- &proxy_canonical_name);
+ &proxy_canonical_name, cfg->addressfamily);
if (sk_addr_error(proxy_addr) != NULL) {
ret->error = "Proxy error: Unable to resolve proxy host name";
return (Socket)ret;
}
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
- const Config *cfg)
+ const Config *cfg, int addressfamily)
{
/* TODO: SOCKS (and potentially others) support inbound
* TODO: connections via the proxy. support them.
*/
- return sk_newlistener(srcaddr, port, plug, local_host_only);
+ return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);
}
/* ----------------------------------------------------------------------
bump("Empty host name");
/*
+ * Remove fiddly bits of address: remove a colon suffix, and
+ * the square brackets around an IPv6 literal address.
+ */
+ if (host[0] == '[') {
+ host++;
+ host[strcspn(host, "]")] = '\0';
+ } else {
+ host[strcspn(host, ":")] = '\0';
+ }
+
+ /*
* If we haven't loaded session details already (e.g., from -load),
* try looking for a session called "host".
*/
}
/*
- * Trim a colon suffix off the hostname if it's there.
- */
- cfg.host[strcspn(cfg.host, ":")] = '\0';
-
- /*
* Remove any remaining whitespace from the hostname.
*/
{
*/
static char *colon(char *str)
{
+ /* Check and process IPv6 literal addresses
+ * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */
+ char *ipv6 = strchr(str, '[');
+ if (ipv6) {
+ str = strchr(str, ']');
+ if (str) {
+ /* Terminate on the closing bracket */
+ *str++ = '\0';
+ return (str);
+ }
+ return (NULL);
+ }
+
/* We ignore a leading colon, since the hostname cannot be
empty. We also ignore a colon as second character because
of filenames like f:myfile.txt. */
*targ++ = '\0';
if (*targ == '\0')
targ = ".";
- /* Substitute "." for emtpy target */
+ /* Substitute "." for empty target */
/* Separate host and username */
user = host;
printf(" -l user connect with specified username\n");
printf(" -pw passw login with specified password\n");
printf(" -1 -2 force use of particular SSH protocol version\n");
+ printf(" -4 -6 force use of IPv4 or IPv6\n");
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
printf(" -batch disable all interactive prompts\n");
printf(" -P port connect to specified port\n");
printf(" -pw passw login with specified password\n");
printf(" -1 -2 force use of particular SSH protocol version\n");
+ printf(" -4 -6 force use of IPv4 or IPv6\n");
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
printf(" -batch disable all interactive prompts\n");
FUNKY_SCO
};
+enum {
+ /*
+ * Network address types. Used for specifying choice of IPv4/v6
+ * in config; also used in proxy.c to indicate whether a given
+ * host name has already been resolved or will be resolved at
+ * the proxy end.
+ */
+ ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME
+};
+
struct backend_tag {
const char *(*init) (void *frontend_handle, void **backend_handle,
Config *cfg,
char host[512];
int port;
int protocol;
+ int addressfamily;
int close_on_exit;
int warn_on_close;
int ping_interval; /* in seconds */
*/
{
char *buf;
- buf = dupprintf("Looking up host \"%s\"", host);
+ buf = dupprintf("Looking up host \"%s\"%s", host,
+ (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ "")));
logevent(raw->frontend, buf);
sfree(buf);
}
- addr = name_lookup(host, port, realhost, cfg);
+ addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
*/
{
char *buf;
- buf = dupprintf("Looking up host \"%s\"", host);
+ buf = dupprintf("Looking up host \"%s\"%s", host,
+ (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ "")));
logevent(rlogin->frontend, buf);
sfree(buf);
}
- addr = name_lookup(host, port, realhost, cfg);
+ addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
write_setting_s(sesskey, "TerminalType", cfg->termtype);
write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);
+ /* Address family selection */
+ write_setting_i(sesskey, "AddressFamily", cfg->addressfamily);
+
/* proxy settings */
write_setting_s(sesskey, "ProxyExcludeList", cfg->proxy_exclude_list);
write_setting_i(sesskey, "ProxyDNS", (cfg->proxy_dns+2)%3);
break;
}
+ /* Address family selection */
+ gppi(sesskey, "AddressFamily", ADDRTYPE_UNSPEC, &cfg->addressfamily);
+
/* The CloseOnExit numbers are arranged in a different order from
* the standard FORCE_ON / FORCE_OFF / AUTO. */
gppi(sesskey, "CloseOnExit", 1, &i); cfg->close_on_exit = (i+1)%3;
unsigned sport, dport;
char *saddr, *daddr;
struct ssh_rportfwd *remote;
+ int addressfamily;
void *local;
};
#define free_portfwd(pf) ( \
/*
* Try to find host.
*/
- logeventf(ssh, "Looking up host \"%s\"", host);
- addr = name_lookup(host, port, realhost, &ssh->cfg);
+ logeventf(ssh, "Looking up host \"%s\"%s", host,
+ (ssh->cfg.addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (ssh->cfg.addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
+ addr = name_lookup(host, port, realhost, &ssh->cfg,
+ ssh->cfg.addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
{
- char type;
+ char address_family, type;
int n;
int sport,dport,sserv,dserv;
char sports[256], dports[256], saddr[256], host[256];
}
while (*portfwd_strptr) {
- type = *portfwd_strptr++;
+ address_family = 'A';
+ type = 'L';
+ while (*portfwd_strptr && *portfwd_strptr != '\t') {
+ if (*portfwd_strptr == 'A' ||
+ *portfwd_strptr == '4' ||
+ *portfwd_strptr == '6')
+ address_family = *portfwd_strptr;
+ else if (*portfwd_strptr == 'L' ||
+ *portfwd_strptr == 'R' ||
+ *portfwd_strptr == 'D')
+ type = *portfwd_strptr;
+ portfwd_strptr++;
+ }
+
saddr[0] = '\0';
+
n = 0;
while (*portfwd_strptr && *portfwd_strptr != '\t') {
if (*portfwd_strptr == ':') {
pfrec->dport = dport;
pfrec->local = NULL;
pfrec->remote = NULL;
+ pfrec->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :
+ address_family == '6' ? ADDRTYPE_IPV6 :
+ ADDRTYPE_UNSPEC);
epfrec = add234(ssh->portfwds, pfrec);
if (epfrec != pfrec) {
const char *err = pfd_addforward(host, dport,
*saddr ? saddr : NULL,
sport, ssh, &ssh->cfg,
- &pfrec->local);
- if (err) {
- logeventf(ssh, "Local port %s forward to %s"
- " failed: %s", sportdesc, dportdesc, err);
- } else {
- logeventf(ssh, "Local port %s forwarding to %s",
- sportdesc, dportdesc);
- }
+ &pfrec->local,
+ pfrec->addressfamily);
+
+ logeventf(ssh, "Local %sport %s forward to %s%s%s",
+ pfrec->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
+ pfrec->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
+ sportdesc, dportdesc,
+ err ? " failed: " : "", err);
+
sfree(dportdesc);
} else if (type == 'D') {
const char *err = pfd_addforward(NULL, -1,
*saddr ? saddr : NULL,
sport, ssh, &ssh->cfg,
- &pfrec->local);
- if (err) {
- logeventf(ssh, "Local port %s SOCKS dynamic forward"
- " setup failed: %s", sportdesc, err);
- } else {
- logeventf(ssh, "Local port %s doing SOCKS"
- " dynamic forwarding", sportdesc);
- }
+ &pfrec->local,
+ pfrec->addressfamily);
+
+ logeventf(ssh, "Local %sport %s SOCKS dynamic forward%s%s",
+ pfrec->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
+ pfrec->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
+ sportdesc,
+ err ? " setup failed: " : "", err);
} else {
struct ssh_rportfwd *pf;
/* Remote side is trying to open a channel to talk to a
* forwarded port. Give them back a local channel number. */
struct ssh_channel *c;
- struct ssh_rportfwd pf;
+ struct ssh_rportfwd pf, *pfp;
int remoteid;
int hostsize, port;
char *host, buf[1024];
memcpy(pf.dhost, host, hostsize);
pf.dhost[hostsize] = '\0';
pf.dport = port;
+ pfp = find234(ssh->rportfwds, &pf, NULL);
- if (find234(ssh->rportfwds, &pf, NULL) == NULL) {
+ if (pfp == NULL) {
sprintf(buf, "Rejected remote port open request for %s:%d",
pf.dhost, port);
logevent(buf);
pf.dhost, port);
logevent(buf);
e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
- c, &ssh->cfg);
+ c, &ssh->cfg, pfp->pfrec->addressfamily);
if (e != NULL) {
char buf[256];
sprintf(buf, "Port open failed: %s", e);
const char *e = pfd_newconnect(&c->u.pfd.s,
realpf->dhost,
realpf->dport, c,
- &ssh->cfg);
+ &ssh->cfg,
+ realpf->pfrec->addressfamily);
logeventf(ssh, "Attempting to forward remote port to "
"%s:%d", realpf->dhost, realpf->dport);
if (e != NULL) {
/* Exports from portfwd.c */
extern const char *pfd_newconnect(Socket * s, char *hostname, int port,
- void *c, const Config *cfg);
+ void *c, const Config *cfg,
+ int addressfamily);
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
int port, void *backhandle,
- const Config *cfg, void **sockdata);
+ const Config *cfg, void **sockdata,
+ int address_family);
extern void pfd_close(Socket s);
extern void pfd_terminate(void *sockdata);
extern int pfd_send(Socket s, char *data, int len);
*/
{
char *buf;
- buf = dupprintf("Looking up host \"%s\"", host);
+ buf = dupprintf("Looking up host \"%s\"%s", host,
+ (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
+ (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
+ "")));
logevent(telnet->frontend, buf);
sfree(buf);
}
- addr = name_lookup(host, port, realhost, &telnet->cfg);
+ addr = name_lookup(host, port, realhost, &telnet->cfg, cfg->addressfamily);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
* in this SockAddr structure.
*/
int family;
-#ifdef IPV6
+#ifndef NO_IPV6
struct addrinfo *ai; /* Address IPv6 style. */
#else
unsigned long address; /* Address IPv4 style. */
return strerror(error);
}
-SockAddr sk_namelookup(const char *host, char **canonicalname)
+SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family)
{
SockAddr ret = snew(struct SockAddr_tag);
-#ifdef IPV6
+#ifndef NO_IPV6
struct addrinfo hints;
int err;
#else
*realhost = '\0';
ret->error = NULL;
-#ifdef IPV6
+#ifndef NO_IPV6
hints.ai_flags = AI_CANONNAME;
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = address_family;
hints.ai_socktype = 0;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
strncpy(buf, addr->hostname, buflen);
buf[buflen-1] = '\0';
} else {
-#ifdef IPV6
+#ifndef NO_IPV6
if (getnameinfo(addr->ai->ai_addr, addr->ai->ai_addrlen, buf, buflen,
NULL, 0, NI_NUMERICHOST) != 0) {
buf[0] = '\0';
if (addr->family == AF_UNSPEC)
return 0; /* we don't know; assume not */
else {
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET)
return ipv4_is_loopback(
((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr);
int sk_addrtype(SockAddr addr)
{
return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
-#ifdef IPV6
+#ifndef NO_IPV6
addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
#endif
ADDRTYPE_NAME);
void sk_addrcopy(SockAddr addr, char *buf)
{
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET)
memcpy(buf, &((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr,
sizeof(struct in_addr));
void sk_addr_free(SockAddr addr)
{
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->ai != NULL)
freeaddrinfo(addr->ai);
#endif
int nodelay, int keepalive, Plug plug)
{
int s;
-#ifdef IPV6
+#ifndef NO_IPV6
struct sockaddr_in6 a6;
#endif
struct sockaddr_in a;
/* BSD IP stacks need sockaddr_in zeroed before filling in */
memset(&a,'\0',sizeof(struct sockaddr_in));
-#ifdef IPV6
+#ifndef NO_IPV6
memset(&a6,'\0',sizeof(struct sockaddr_in6));
#endif
while (1) {
int retcode;
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET6) {
/* XXX use getaddrinfo to get a local address? */
a6.sin6_family = AF_INET6;
* Connect to remote address.
*/
switch(addr->family) {
-#ifdef IPV6
+#ifndef NO_IPV6
case AF_INET:
/* XXX would be better to have got getaddrinfo() to fill in the port. */
((struct sockaddr_in *)addr->ai->ai_addr)->sin_port =
return (Socket) ret;
}
-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 address_family)
{
int s;
-#ifdef IPV6
-#if 0
- struct sockaddr_in6 a6;
-#endif
+#ifndef NO_IPV6
struct addrinfo hints, *ai;
char portstr[6];
#endif
ret->listener = 1;
/*
+ * Translate address_family from platform-independent constants
+ * into local reality.
+ */
+ address_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+ address_family == ADDRTYPE_IPV6 ? AF_INET6 : AF_UNSPEC);
+
+#ifndef NO_IPV6
+ /* Let's default to IPv6.
+ * If the stack doesn't support IPv6, we will fall back to IPv4. */
+ 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
+
+ /*
* Open socket.
*/
- s = socket(AF_INET, SOCK_STREAM, 0);
+ s = socket(address_family, SOCK_STREAM, 0);
+
+ /* If the host doesn't support IPv6 try fallback to IPv4. */
+ if (s < 0 && address_family == AF_INET6) {
+ address_family = AF_INET;
+ s = socket(address_family, SOCK_STREAM, 0);
+ }
ret->s = s;
if (s < 0) {
/* BSD IP stacks need sockaddr_in zeroed before filling in */
memset(&a,'\0',sizeof(struct sockaddr_in));
-#ifdef IPV6
+#ifndef NO_IPV6
#if 0
memset(&a6,'\0',sizeof(struct sockaddr_in6));
#endif
hints.ai_flags = AI_NUMERICHOST;
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = address_family;
hints.ai_socktype = 0;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
printf(" -A -a enable / disable agent forwarding\n");
printf(" -t -T enable / disable pty allocation\n");
printf(" -1 -2 force use of particular protocol version\n");
+ printf(" -4 -6 force use of IPv4 or IPv6\n");
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
END
/* Accelerators used: aco */
-IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 280, 252
+IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 300, 252
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "PuTTY Configuration"
FONT 8, "MS Shell Dlg"
* Otherwise, we're creating the controls for a particular
* panel.
*/
- ctlposinit(&cp, hwnd, 80, 3, 13);
+ ctlposinit(&cp, hwnd, 100, 3, 13);
wc = &ctrls_panel;
base_id = IDCX_PANELBASE;
}
HWND tvstatic;
r.left = 3;
- r.right = r.left + 75;
+ r.right = r.left + 95;
r.top = 3;
r.bottom = r.top + 10;
MapDialogRect(hwnd, &r);
SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
r.left = 3;
- r.right = r.left + 75;
+ r.right = r.left + 95;
r.top = 13;
r.bottom = r.top + 219;
MapDialogRect(hwnd, &r);
}
/*
- * Trim a colon suffix off the hostname if it's there.
+ * Trim a colon suffix off the hostname if it's there. In
+ * order to protect IPv6 address literals against this
+ * treatment, we do not do this if there's _more_ than one
+ * colon.
*/
- cfg.host[strcspn(cfg.host, ":")] = '\0';
+ {
+ char *c = strchr(cfg.host, ':');
+
+ if (c) {
+ char *d = strchr(c+1, ':');
+ if (!d)
+ *c = '\0';
+ }
+ }
/*
* Remove any remaining whitespace from the hostname.
#define WINHELP_CTX_connection_username "connection.username"
#define WINHELP_CTX_connection_keepalive "connection.keepalive"
#define WINHELP_CTX_connection_nodelay "connection.nodelay"
+#define WINHELP_CTX_connection_ipversion "connection.ipversion"
#define WINHELP_CTX_connection_tcpkeepalive "connection.tcpkeepalive"
#define WINHELP_CTX_proxy_type "proxy.type"
#define WINHELP_CTX_proxy_main "proxy.main"
#define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth"
#define WINHELP_CTX_ssh_tunnels_portfwd "ssh.tunnels.portfwd"
#define WINHELP_CTX_ssh_tunnels_portfwd_localhost "ssh.tunnels.portfwd.localhost"
+#define WINHELP_CTX_ssh_tunnels_portfwd_ipversion "ssh.tunnels.portfwd.ipversion"
#define WINHELP_CTX_ssh_bugs_ignore1 "ssh.bugs.ignore1"
#define WINHELP_CTX_ssh_bugs_plainpw1 "ssh.bugs.plainpw1"
#define WINHELP_CTX_ssh_bugs_rsa1 "ssh.bugs.rsa1"
char *user;
namelen = 0;
- if (GetUserName(NULL, &namelen) == FALSE)
- return NULL;
+ if (GetUserName(NULL, &namelen) == FALSE) {
+ /*
+ * Apparently this doesn't work at least on Windows XP SP2.
+ * Thus assume a maximum of 256. It will fail again if it
+ * doesn't fit.
+ */
+ namelen = 256;
+ }
user = snewn(namelen, char);
GetUserName(user, &namelen);
/*
* Windows networking abstraction.
*
- * Due to this clean abstraction it was possible
- * to easily implement IPv6 support :)
- *
- * IPv6 patch 1 (27 October 2000) Jeroen Massar <jeroen@unfix.org>
- * - Preliminary hacked IPv6 support.
- * - Connecting to IPv6 address (eg fec0:4242:4242:100:2d0:b7ff:fe8f:5d42) works.
- * - Connecting to IPv6 hostname (eg heaven.ipv6.unfix.org) works.
- * - Compiles as either IPv4 or IPv6.
- *
- * IPv6 patch 2 (29 October 2000) Jeroen Massar <jeroen@unfix.org>
- * - When compiled as IPv6 it also allows connecting to IPv4 hosts.
- * - Added some more documentation.
- *
- * IPv6 patch 3 (18 November 2000) Jeroen Massar <jeroen@unfix.org>
- * - It now supports dynamically loading the IPv6 resolver dll's.
- * This way we should be able to distribute one (1) binary
- * which supports both IPv4 and IPv6.
- * - getaddrinfo() and getnameinfo() are loaded dynamicaly if possible.
- * - in6addr_any is defined in this file so we don't need to link to wship6.lib
- * - The patch is now more unified so that we can still
- * remove all IPv6 support by undef'ing IPV6.
- * But where it fallsback to IPv4 it uses the IPv4 code which is already in place...
- * - Canonical name resolving works.
- *
- * IPv6 patch 4 (07 January 2001) Jeroen Massar <jeroen@unfix.org>
- * - patch against CVS of today, will be submitted to the bugs list
- * as a 'cvs diff -u' on Simon's request...
- *
+ * For the IPv6 code in here I am indebted to Jeroen Massar and
+ * unfix.org.
*/
-/*
- * Define IPV6 to have IPv6 on-the-fly-loading support.
- * This means that one doesn't have to have an IPv6 stack to use it.
- * But if an IPv6 stack is found it is used with a fallback to IPv4.
- */
-/* #define IPV6 1 */
-
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "tree234.h"
#include <ws2tcpip.h>
-#ifdef IPV6
-#include <tpipv6.h>
+
+#ifndef NO_IPV6
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
#endif
#define ipv4_is_loopback(addr) \
* 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;
unsigned long address; /* Address IPv4 style. */
-#ifdef IPV6
- struct addrinfo *ai; /* Address IPv6 style. */
+#ifndef NO_IPV6
+ struct addrinfo *ai; /* Address AF-independent (IPv4+IPv6) style. */
#endif
char hostname[512]; /* Store an unresolved host name. */
};
}
}
-SockAddr sk_namelookup(const char *host, char **canonicalname)
+SockAddr sk_namelookup(const char *host, char **canonicalname,
+ int address_family)
{
SockAddr ret = snew(struct SockAddr_tag);
unsigned long a;
/* 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->family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+#ifndef NO_IPV6
+ address_family == ADDRTYPE_IPV6 ? AF_INET6 :
+#endif
+ AF_UNSPEC);
*realhost = '\0';
if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
-#ifdef IPV6
+#ifndef NO_IPV6
/* Try to get the getaddrinfo() function from wship6.dll */
/* This way one doesn't need to have IPv6 dll's to use PuTTY and
"getaddrinfo");
/*
- * Use fGetAddrInfo when it's available (which usually also
- * means IPv6 is installed...)
+ * Use fGetAddrInfo when it's available
*/
if (fGetAddrInfo) {
- /*debug(("Resolving \"%s\" with getaddrinfo() (IPv4+IPv6 capable)...\n", host)); */
- if (fGetAddrInfo(host, NULL, NULL, &ret->ai) == 0)
- ret->family = ret->ai->ai_family;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ret->family;
+ if (fGetAddrInfo(host, NULL, &hints, &ret->ai) == 0)
+ ret->family = ret->ai->ai_family;
} else
#endif
{
/*
* Otherwise use the IPv4-only gethostbyname...
- * (NOTE: we don't use gethostbyname as a
- * fallback!)
+ * (NOTE: we don't use gethostbyname as a fallback!)
*/
if (ret->family == 0) {
- /*debug(("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host)); */
if ( (h = p_gethostbyname(host)) )
ret->family = AF_INET;
}
}
- /*debug(("Done resolving...(family is %d) AF_INET = %d, AF_INET6 = %d\n", ret->family, AF_INET, AF_INET6)); */
- if (ret->family == 0) {
+ if (ret->family == AF_UNSPEC) {
DWORD err = p_WSAGetLastError();
ret->error = (err == WSAENETDOWN ? "Network is down" :
err ==
WSAHOST_NOT_FOUND ? "Host does not exist" : err
== WSATRY_AGAIN ? "Host not found" :
-#ifdef IPV6
+#ifndef NO_IPV6
fGetAddrInfo ? "getaddrinfo: unknown error" :
#endif
"gethostbyname: unknown error");
-#ifdef DEBUG
- {
- LPVOID lpMsgBuf;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) & lpMsgBuf, 0, NULL);
- /*debug(("Error %ld: %s (h=%lx)\n", err, lpMsgBuf, h)); */
- /* Free the buffer. */
- LocalFree(lpMsgBuf);
- }
-#endif
} else {
ret->error = NULL;
-#ifdef IPV6
+#ifndef NO_IPV6
/* If we got an address info use that... */
if (ret->ai) {
typedef int (CALLBACK * FGETNAMEINFO)
strncpy(realhost, h->h_name, sizeof(realhost));
}
}
-#ifdef IPV6
+#ifndef NO_IPV6
FreeLibrary(dllWSHIP6);
#endif
} else {
void sk_getaddr(SockAddr addr, char *buf, int buflen)
{
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET6) {
- FIXME; /* I don't know how to get a text form of an IPv6 address. */
+ /* Try to get the WSAAddressToStringA() function from wship6.dll */
+ /* This way one doesn't need to have IPv6 dll's to use PuTTY and
+ * it will fallback to IPv4. */
+ typedef int (CALLBACK * FADDRTOSTR) (LPSOCKADDR lpsaAddress,
+ DWORD dwAddressLength,
+ LPWSAPROTOCOL_INFO lpProtocolInfo,
+ OUT LPTSTR lpszAddressString,
+ IN OUT LPDWORD lpdwAddressStringLength
+ );
+ FADDRTOSTR fAddrToStr = NULL;
+
+ HINSTANCE dllWS2 = LoadLibrary("ws2_32.dll");
+ if (dllWS2) {
+ fAddrToStr = (FADDRTOSTR)GetProcAddress(dllWS2,
+ "WSAAddressToStringA");
+ if (fAddrToStr) {
+ fAddrToStr(addr->ai->ai_addr, addr->ai->ai_addrlen,
+ NULL, buf, &buflen);
+ }
+ else strncpy(buf, "IPv6", buflen);
+ FreeLibrary(dllWS2);
+ }
} else
#endif
if (addr->family == AF_INET) {
strncpy(buf, p_inet_ntoa(a), buflen);
buf[buflen-1] = '\0';
} else {
- assert(addr->family == AF_UNSPEC);
strncpy(buf, addr->hostname, buflen);
buf[buflen-1] = '\0';
}
int sk_address_is_local(SockAddr addr)
{
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET6) {
- FIXME; /* someone who can compile for IPV6 had better do this bit */
+ return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)addr->ai->ai_addr);
} else
#endif
if (addr->family == AF_INET) {
int sk_addrtype(SockAddr addr)
{
return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
-#ifdef IPV6
+#ifndef NO_IPV6
addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
#endif
ADDRTYPE_NAME);
void sk_addrcopy(SockAddr addr, char *buf)
{
assert(addr->family != AF_UNSPEC);
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET6) {
memcpy(buf, (char*) addr->ai, 16);
} else
};
SOCKET s;
-#ifdef IPV6
+#ifndef NO_IPV6
SOCKADDR_IN6 a6;
#endif
SOCKADDR_IN a;
/*
* Open socket.
*/
- assert(addr->family != 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 (addr->family == AF_UNSPEC) addr->family = AF_INET6;
+#else
+ /* No other choice, default to IPv4 */
+ if (addr->family == AF_UNSPEC) addr->family = AF_INET;
+#endif
s = p_socket(addr->family, SOCK_STREAM, 0);
ret->s = s;
while (1) {
int retcode;
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET6) {
memset(&a6, 0, sizeof(a6));
a6.sin6_family = AF_INET6;
-/*a6.sin6_addr = in6addr_any; *//* == 0 */
+ /*a6.sin6_addr = in6addr_any; */ /* == 0 done by memset() */
a6.sin6_port = p_htons(localport);
} else
#endif
a.sin_addr.s_addr = p_htonl(INADDR_ANY);
a.sin_port = p_htons(localport);
}
-#ifdef IPV6
+#ifndef NO_IPV6
retcode = p_bind(s, (addr->family == AF_INET6 ?
(struct sockaddr *) &a6 :
(struct sockaddr *) &a),
/*
* Connect to remote address.
*/
-#ifdef IPV6
+#ifndef NO_IPV6
if (addr->family == AF_INET6) {
memset(&a, 0, sizeof(a));
a6.sin6_family = AF_INET6;
}
if ((
-#ifdef IPV6
+#ifndef NO_IPV6
p_connect(s, ((addr->family == AF_INET6) ?
(struct sockaddr *) &a6 : (struct sockaddr *) &a),
(addr->family == AF_INET6) ? sizeof(a6) : sizeof(a))
return (Socket) ret;
}
-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 address_family)
{
static const struct socket_function_table fn_table = {
sk_tcp_plug,
};
SOCKET s;
-#ifdef IPV6
+#ifndef NO_IPV6
SOCKADDR_IN6 a6;
#endif
SOCKADDR_IN a;
+
DWORD err;
char *errstr;
Actual_Socket ret;
ret->pending_error = 0;
/*
+ * Translate address_family from platform-independent constants
+ * into local reality.
+ */
+ address_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+#ifndef NO_IPV6
+ 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
+
+ /*
* Open socket.
*/
- s = p_socket(AF_INET, SOCK_STREAM, 0);
+ s = p_socket(address_family, SOCK_STREAM, 0);
ret->s = s;
if (s == INVALID_SOCKET) {
p_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
-#ifdef IPV6
- if (addr->family == AF_INET6) {
+#ifndef NO_IPV6
+ if (address_family == AF_INET6) {
memset(&a6, 0, sizeof(a6));
a6.sin6_family = AF_INET6;
/* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't
- * know how to do it. :-) */
+ * know how to do it. :-)
+ * (jeroen:) saddr is specified as an address.. eg 2001:db8::1
+ * Thus we need either a parser that understands [2001:db8::1]:80
+ * style addresses and/or enhance this to understand hostnames too. */
if (local_host_only)
a6.sin6_addr = in6addr_loopback;
else
a.sin_port = p_htons((short)port);
}
-#ifdef IPV6
- retcode = p_bind(s, (addr->family == AF_INET6 ?
+#ifndef NO_IPV6
+ retcode = p_bind(s, (address_family == AF_INET6 ?
(struct sockaddr *) &a6 :
(struct sockaddr *) &a),
- (addr->family ==
+ (address_family ==
AF_INET6 ? sizeof(a6) : sizeof(a)));
#else
retcode = p_bind(s, (struct sockaddr *) &a, sizeof(a));
return open;
case FD_ACCEPT:
{
+#ifdef NO_IPV6
struct sockaddr_in isa;
- int addrlen = sizeof(struct sockaddr_in);
+#else
+ struct sockaddr_storage isa;
+#endif
+ int addrlen = sizeof(isa);
SOCKET t; /* socket of connection */
- memset(&isa, 0, sizeof(struct sockaddr_in));
+ memset(&isa, 0, sizeof(isa));
err = 0;
t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen);
if (t == INVALID_SOCKET)
if (err == WSATRY_AGAIN)
break;
}
-
+#ifndef NO_IPV6
+ if (isa.ss_family == AF_INET &&
+ s->localhost_only &&
+ !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) {
+#else
if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) {
+#endif
p_closesocket(t); /* dodgy WinSock let nonlocal through */
} else if (plug_accepting(s->plug, (void*)t)) {
p_closesocket(t); /* denied or error */
printf(" -A -a enable / disable agent forwarding\n");
printf(" -t -T enable / disable pty allocation\n");
printf(" -1 -2 force use of particular protocol version\n");
+ printf(" -4 -6 force use of IPv4 or IPv6\n");
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
/*
* Try to find host.
*/
- addr = name_lookup(host, port, &dummy_realhost, cfg);
+ addr = name_lookup(host, port, &dummy_realhost, cfg, ADDRTYPE_UNSPEC);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;