1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-10 07:38:06 -05:00

Implement `portfwd-loopback-choice'. Works on local side in Unix as

well, though it's a lot less useful since you still can't bind to
low-numbered ports of odd loopback IPs. Should work in principle for
SSH2 remote forwardings as well as local ones, but OpenSSH seems
unwilling to cooperate.

[originally from svn r2344]
This commit is contained in:
Simon Tatham 2002-12-18 11:39:25 +00:00
parent 8cb52a26e1
commit 99b870dbc6
9 changed files with 182 additions and 50 deletions

View File

@ -160,7 +160,7 @@ int cmdline_process_param(char *p, char *value, int need_save)
cfg.username[sizeof(cfg.username) - 1] = '\0';
}
if ((!strcmp(p, "-L") || !strcmp(p, "-R"))) {
char *fwd, *ptr, *q;
char *fwd, *ptr, *q, *qq;
int i=0;
RETURN(2);
UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER);
@ -180,8 +180,23 @@ int cmdline_process_param(char *p, char *value, int need_save)
return ret;
}
strncpy(ptr+1, fwd, sizeof(cfg.portfwd) - i);
q = strchr(ptr, ':');
if (q) *q = '\t'; /* replace first : with \t */
/*
* We expect _at least_ two colons in this string. The
* possible formats are `sourceport:desthost:destport', or
* `sourceip:sourceport:desthost:destport' if you're
* specifying a particular loopback address. We need to
* replace the one between source and dest with a \t; this
* means we must find the second-to-last colon in the
* string.
*/
q = qq = strchr(ptr, ':');
while (qq) {
char *qqq = strchr(qq+1, ':');
if (qqq)
q = qq;
qq = qqq;
}
if (q) *q = '\t'; /* replace second-last colon with \t */
cfg.portfwd[sizeof(cfg.portfwd) - 1] = '\0';
cfg.portfwd[sizeof(cfg.portfwd) - 2] = '\0';
ptr[strlen(ptr)+1] = '\000'; /* append two '\000' */

View File

@ -1,4 +1,4 @@
\versionid $Id: config.but,v 1.44 2002/10/22 09:40:38 simon Exp $
\versionid $Id: config.but,v 1.45 2002/12/18 11:39:25 simon Exp $
\C{config} Configuring PuTTY
@ -1881,6 +1881,19 @@ in the list box.
To remove a port forwarding, simply select its details in the list
box, and click the \q{Remove} button.
In the \q{Source port} box, you can also optionally enter an IP
address to listen on. Typically a Windows machine can be asked to
listen on any single IP address in the \cw{127.*.*.*} range, and all
of these are loopback addresses available only to the local machine.
So if you forward (for example) \c{127.0.0.5:79} to a remote
machine's \cw{finger} port, then you should be able to run commands
such as \c{finger fred@127.0.0.5}. This can be useful if the program
connecting to the forwarded port doesn't allow you to change the
port number it uses. This feature is available for local-to-remote
forwarded ports; SSH1 is unable to support it for remote-to-local
ports, while SSH2 can support it in theory but servers will not
necessarily cooperate.
\S{config-ssh-portfwd-localhost} Controlling the visibility of
forwarded ports

View File

@ -1,4 +1,4 @@
\versionid $Id: using.but,v 1.8 2002/09/11 17:30:36 jacob Exp $
\versionid $Id: using.but,v 1.9 2002/12/18 11:39:25 simon Exp $
\C{using} Using PuTTY
@ -254,7 +254,8 @@ to a port on a remote server, you need to:
\b Choose a port number on your local machine where PuTTY should
listen for incoming connections. There are likely to be plenty of
unused port numbers above 3000.
unused port numbers above 3000. (You can also use a local loopback
address here; see \k{config-ssh-portfwd} for more details.)
\b Now, before you start your SSH connection, go to the Tunnels
panel (see \k{config-ssh-portfwd}). Make sure the \q{Local} radio

View File

@ -67,7 +67,7 @@ struct plug_function_table {
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug);
Socket new_listener(int port, Plug plug, int local_host_only);
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only);
/* socket functions */
@ -84,7 +84,7 @@ void sk_addr_free(SockAddr addr);
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, Plug p);
Socket sk_newlistener(int port, Plug plug, int local_host_only);
Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only);
Socket sk_register(void *sock, Plug plug);

View File

@ -203,9 +203,10 @@ static int pfd_accepting(Plug p, void *sock)
/* Add a new forwarding from port -> desthost:destport
sets up a listener on the local machine on port
sets up a listener on the local machine on (srcaddr:)port
*/
char *pfd_addforward(char *desthost, int destport, int port, void *backhandle)
char *pfd_addforward(char *desthost, int destport, char *srcaddr, int port,
void *backhandle)
{
static struct plug_function_table fn_table = {
pfd_closing,
@ -231,7 +232,7 @@ char *pfd_addforward(char *desthost, int destport, int port, void *backhandle)
pr->waiting = NULL;
pr->backhandle = backhandle;
pr->s = s = new_listener(port, (Plug) pr, !cfg.lport_acceptall);
pr->s = s = new_listener(srcaddr, port, (Plug) pr, !cfg.lport_acceptall);
if ((err = sk_socket_error(s))) {
sfree(pr);
return err;

View File

@ -404,13 +404,13 @@ Socket new_connection(SockAddr addr, char *hostname,
return sk_new(addr, port, privport, oobinline, nodelay, plug);
}
Socket new_listener(int port, Plug plug, int local_host_only)
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only)
{
/* TODO: SOCKS (and potentially others) support inbound
* TODO: connections via the proxy. support them.
*/
return sk_newlistener(port, plug, local_host_only);
return sk_newlistener(srcaddr, port, plug, local_host_only);
}
/* ----------------------------------------------------------------------

99
ssh.c
View File

@ -299,8 +299,8 @@ extern void x11_unthrottle(Socket s);
extern void x11_override_throttle(Socket s, int enable);
extern char *pfd_newconnect(Socket * s, char *hostname, int port, void *c);
extern char *pfd_addforward(char *desthost, int destport, int port,
void *backhandle);
extern char *pfd_addforward(char *desthost, int destport, char *srcaddr,
int port, void *backhandle);
extern void pfd_close(Socket s);
extern int pfd_send(Socket s, char *data, int len);
extern void pfd_confirm(Socket s);
@ -3071,28 +3071,46 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
char type;
int n;
int sport,dport,sserv,dserv;
char sports[256], dports[256], host[256];
char sports[256], dports[256], saddr[256], host[256];
ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
/* Add port forwardings. */
ssh->portfwd_strptr = cfg.portfwd;
while (*ssh->portfwd_strptr) {
type = *ssh->portfwd_strptr++;
saddr[0] = '\0';
n = 0;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t')
sports[n++] = *ssh->portfwd_strptr++;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t') {
if (*ssh->portfwd_strptr == ':') {
/*
* We've seen a colon in the middle of the
* source port number. This means that
* everything we've seen until now is the
* source _address_, so we'll move it into
* saddr and start sports from the beginning
* again.
*/
ssh->portfwd_strptr++;
sports[n] = '\0';
strcpy(saddr, sports);
n = 0;
}
if (n < 255) sports[n++] = *ssh->portfwd_strptr++;
}
sports[n] = 0;
if (*ssh->portfwd_strptr == '\t')
ssh->portfwd_strptr++;
n = 0;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':')
host[n++] = *ssh->portfwd_strptr++;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') {
if (n < 255) host[n++] = *ssh->portfwd_strptr++;
}
host[n] = 0;
if (*ssh->portfwd_strptr == ':')
ssh->portfwd_strptr++;
n = 0;
while (*ssh->portfwd_strptr)
dports[n++] = *ssh->portfwd_strptr++;
while (*ssh->portfwd_strptr) {
if (n < 255) dports[n++] = *ssh->portfwd_strptr++;
}
dports[n] = 0;
ssh->portfwd_strptr++;
dport = atoi(dports);
@ -3117,9 +3135,12 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
}
if (sport && dport) {
if (type == 'L') {
pfd_addforward(host, dport, sport, ssh);
logeventf(ssh, "Local port %.*s%.*s%d%.*s forwarding to"
" %s:%.*s%.*s%d%.*s",
pfd_addforward(host, dport, *saddr ? saddr : NULL,
sport, ssh);
logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
" forwarding to %s:%.*s%.*s%d%.*s",
(int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
(int)(*saddr?1:0), ":",
(int)(sserv ? strlen(sports) : 0), sports,
sserv, "(", sport, sserv, ")",
host,
@ -3130,6 +3151,11 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
pf = smalloc(sizeof(*pf));
strcpy(pf->dhost, host);
pf->dport = dport;
if (saddr) {
logeventf(ssh,
"SSH1 cannot handle source address spec \"%s:%d\"; ignoring",
saddr, sport);
}
if (add234(ssh->rportfwds, pf) != pf) {
logeventf(ssh,
"Duplicate remote port forwarding to %s:%d",
@ -5102,28 +5128,46 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
char type;
int n;
int sport,dport,sserv,dserv;
char sports[256], dports[256], host[256];
char sports[256], dports[256], saddr[256], host[256];
ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
/* Add port forwardings. */
ssh->portfwd_strptr = cfg.portfwd;
while (*ssh->portfwd_strptr) {
type = *ssh->portfwd_strptr++;
saddr[0] = '\0';
n = 0;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t')
sports[n++] = *ssh->portfwd_strptr++;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t') {
if (*ssh->portfwd_strptr == ':') {
/*
* We've seen a colon in the middle of the
* source port number. This means that
* everything we've seen until now is the
* source _address_, so we'll move it into
* saddr and start sports from the beginning
* again.
*/
ssh->portfwd_strptr++;
sports[n] = '\0';
strcpy(saddr, sports);
n = 0;
}
if (n < 255) sports[n++] = *ssh->portfwd_strptr++;
}
sports[n] = 0;
if (*ssh->portfwd_strptr == '\t')
ssh->portfwd_strptr++;
n = 0;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':')
host[n++] = *ssh->portfwd_strptr++;
while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') {
if (n < 255) host[n++] = *ssh->portfwd_strptr++;
}
host[n] = 0;
if (*ssh->portfwd_strptr == ':')
ssh->portfwd_strptr++;
n = 0;
while (*ssh->portfwd_strptr)
dports[n++] = *ssh->portfwd_strptr++;
while (*ssh->portfwd_strptr) {
if (n < 255) dports[n++] = *ssh->portfwd_strptr++;
}
dports[n] = 0;
ssh->portfwd_strptr++;
dport = atoi(dports);
@ -5148,9 +5192,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
}
if (sport && dport) {
if (type == 'L') {
pfd_addforward(host, dport, sport, ssh);
logeventf(ssh, "Local port %.*s%.*s%d%.*s forwarding to"
" %s:%.*s%.*s%d%.*s",
pfd_addforward(host, dport, *saddr ? saddr : NULL,
sport, ssh);
logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
" forwarding to %s:%.*s%.*s%d%.*s",
(int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
(int)(*saddr?1:0), ":",
(int)(sserv ? strlen(sports) : 0), sports,
sserv, "(", sport, sserv, ")",
host,
@ -5167,8 +5214,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
" to %s:%d", host, dport);
sfree(pf);
} else {
logeventf(ssh, "Requesting remote port %.*s%.*s%d%.*s"
logeventf(ssh, "Requesting remote port "
"%.*s%.*s%.*s%.*s%d%.*s"
" forward to %s:%.*s%.*s%d%.*s",
(int)(*saddr?strlen(saddr):0),
*saddr?saddr:NULL,
(int)(*saddr?1:0), ":",
(int)(sserv ? strlen(sports) : 0), sports,
sserv, "(", sport, sserv, ")",
host,
@ -5177,6 +5228,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
ssh2_pkt_init(ssh, SSH2_MSG_GLOBAL_REQUEST);
ssh2_pkt_addstring(ssh, "tcpip-forward");
ssh2_pkt_addbool(ssh, 1);/* want reply */
if (*saddr)
ssh2_pkt_addstring(ssh, saddr);
if (cfg.rport_acceptall)
ssh2_pkt_addstring(ssh, "0.0.0.0");
else

View File

@ -21,6 +21,8 @@
#include "network.h"
#include "tree234.h"
#define ipv4_is_loopback(addr) (inet_netof(addr) == IN_LOOPBACKNET)
struct Socket_tag {
struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
@ -474,7 +476,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
return (Socket) ret;
}
Socket sk_newlistener(int port, Plug plug, int local_host_only)
Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
{
static struct socket_function_table fn_table = {
sk_tcp_plug,
@ -534,6 +536,8 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
if (addr->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. :-) */
if (local_host_only)
a6.sin6_addr = in6addr_loopback;
else
@ -542,11 +546,32 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
} else
#endif
{
int got_addr = 0;
a.sin_family = AF_INET;
if (local_host_only)
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else
a.sin_addr.s_addr = htonl(INADDR_ANY);
/*
* Bind to source address. First try an explicitly
* specified one...
*/
if (srcaddr) {
a.sin_addr.s_addr = inet_addr(srcaddr);
if (a.sin_addr.s_addr != INADDR_NONE) {
/* Override localhost_only with specified listen addr. */
ret->localhost_only = ipv4_is_loopback(a.sin_addr);
got_addr = 1;
}
}
/*
* ... and failing that, go with one of the standard ones.
*/
if (!got_addr) {
if (local_host_only)
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else
a.sin_addr.s_addr = htonl(INADDR_ANY);
}
a.sin_port = htons((short)port);
}
#ifdef IPV6
@ -763,8 +788,7 @@ int select_result(int fd, int event)
break;
}
if (s->localhost_only &&
ntohl(isa.sin_addr.s_addr) != INADDR_LOOPBACK) {
if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) {
close(t); /* someone let nonlocal through?! */
} else if (plug_accepting(s->plug, (void*)t)) {
close(t); /* denied or error */

View File

@ -55,6 +55,9 @@
#include "network.h"
#include "tree234.h"
#define ipv4_is_loopback(addr) \
((ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L)
struct Socket_tag {
struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
@ -654,7 +657,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
return (Socket) ret;
}
Socket sk_newlistener(int port, Plug plug, int local_host_only)
Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
{
static struct socket_function_table fn_table = {
sk_tcp_plug,
@ -716,6 +719,8 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
if (addr->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. :-) */
if (local_host_only)
a6.sin6_addr = in6addr_loopback;
else
@ -724,11 +729,32 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only)
} else
#endif
{
int got_addr = 0;
a.sin_family = AF_INET;
if (local_host_only)
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else
a.sin_addr.s_addr = htonl(INADDR_ANY);
/*
* Bind to source address. First try an explicitly
* specified one...
*/
if (srcaddr) {
a.sin_addr.s_addr = inet_addr(srcaddr);
if (a.sin_addr.s_addr != INADDR_NONE) {
/* Override localhost_only with specified listen addr. */
ret->localhost_only = ipv4_is_loopback(a.sin_addr);
got_addr = 1;
}
}
/*
* ... and failing that, go with one of the standard ones.
*/
if (!got_addr) {
if (local_host_only)
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else
a.sin_addr.s_addr = htonl(INADDR_ANY);
}
a.sin_port = htons((short)port);
}
#ifdef IPV6
@ -1047,8 +1073,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
break;
}
if (s->localhost_only &&
ntohl(isa.sin_addr.s_addr) != INADDR_LOOPBACK) {
if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) {
closesocket(t); /* dodgy WinSock let nonlocal through */
} else if (plug_accepting(s->plug, (void*)t)) {
closesocket(t); /* denied or error */