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);
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' */
-\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
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
-\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
\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
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 */
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);
/* 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,
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;
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);
}
/* ----------------------------------------------------------------------
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);
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);
}
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,
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",
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);
}
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,
" 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,
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
#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 */
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,
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
} 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
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 */
#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 */
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,
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
} 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
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 */