RJK's patch to enable PuTTY's X forwarding to connect to local X

servers using Unix sockets (on Unix only, obviously!).

[originally from svn r4263]
This commit is contained in:
Simon Tatham 2004-05-31 14:01:52 +00:00
Родитель b8e4b6f9b9
Коммит a2acc6ae0d
8 изменённых файлов: 176 добавлений и 81 удалений

Просмотреть файл

@ -611,7 +611,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
gppi(sesskey, "BCE", 1, &cfg->bce); gppi(sesskey, "BCE", 1, &cfg->bce);
gppi(sesskey, "BlinkText", 0, &cfg->blinktext); gppi(sesskey, "BlinkText", 0, &cfg->blinktext);
gppi(sesskey, "X11Forward", 0, &cfg->x11_forward); gppi(sesskey, "X11Forward", 0, &cfg->x11_forward);
gpps(sesskey, "X11Display", "localhost:0", cfg->x11_display, gpps(sesskey, "X11Display", "", cfg->x11_display,
sizeof(cfg->x11_display)); sizeof(cfg->x11_display));
gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth); gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth);

7
ssh.h
Просмотреть файл

@ -281,10 +281,15 @@ extern void x11_unthrottle(Socket s);
extern void x11_override_throttle(Socket s, int enable); extern void x11_override_throttle(Socket s, int enable);
extern int x11_get_screen_number(char *display); extern int x11_get_screen_number(char *display);
void x11_get_real_auth(void *authv, char *display); void x11_get_real_auth(void *authv, char *display);
char *x11_display(const char *display);
/* Platfdorm-dependent X11 function */ /* Platform-dependent X11 functions */
extern void platform_get_x11_auth(char *display, int *proto, extern void platform_get_x11_auth(char *display, int *proto,
unsigned char *data, int *datalen); unsigned char *data, int *datalen);
extern const char platform_x11_best_transport[];
/* best X11 hostname for this platform if none specified */
SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname);
/* make up a SockAddr naming the address for displaynum */
Bignum copybn(Bignum b); Bignum copybn(Bignum b);
Bignum bn_power_2(int n); Bignum bn_power_2(int n);

Просмотреть файл

@ -4,7 +4,9 @@
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h>
#include "putty.h" #include "putty.h"
#include "ssh.h"
void platform_get_x11_auth(char *display, int *protocol, void platform_get_x11_auth(char *display, int *protocol,
unsigned char *data, int *datalen) unsigned char *data, int *datalen)
@ -15,15 +17,19 @@ void platform_get_x11_auth(char *display, int *protocol,
char *localbuf; char *localbuf;
int proto = -1; int proto = -1;
display = x11_display(display);
/* /*
* Normally we should run `xauth list DISPLAYNAME'. However, * Normally we should run `xauth list DISPLAYNAME'. However,
* there's an oddity when the display is local: the display * there's an oddity when the display is local: the display
* `localhost:0' (or `:0') should become just `:0'. * `localhost:0' (or `:0') should become just `:0'.
*/ */
if (!strncmp(display, "localhost:", 10)) if (!strncmp(display, "localhost:", 10)
command = dupprintf("xauth list %s 2>/dev/null", display+9); || !strncmp(display, "unix:", 5))
command = dupprintf("xauth list %s 2>/dev/null",
strchr(display, ':'));
else else
command = dupprintf("xauth list %s 2>/dev/null", display); command = dupprintf("xauth list %s 2>/dev/null", display);
sfree(display);
fp = popen(command, "r"); fp = popen(command, "r");
sfree(command); sfree(command);
@ -113,3 +119,5 @@ void platform_get_x11_auth(char *display, int *protocol,
pclose(fp); pclose(fp);
sfree(localbuf); sfree(localbuf);
} }
const char platform_x11_best_transport[] = "unix";

Просмотреть файл

@ -15,12 +15,17 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netdb.h> #include <netdb.h>
#include <sys/un.h>
#define DEFINE_PLUG_METHOD_MACROS #define DEFINE_PLUG_METHOD_MACROS
#include "putty.h" #include "putty.h"
#include "network.h" #include "network.h"
#include "tree234.h" #include "tree234.h"
#ifndef X11_UNIX_PATH
# define X11_UNIX_PATH "/tmp/.X11-unix/X"
#endif
#define ipv4_is_loopback(addr) (inet_netof(addr) == IN_LOOPBACKNET) #define ipv4_is_loopback(addr) (inet_netof(addr) == IN_LOOPBACKNET)
struct Socket_tag { struct Socket_tag {
@ -380,10 +385,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
struct sockaddr_in6 a6; struct sockaddr_in6 a6;
#endif #endif
struct sockaddr_in a; struct sockaddr_in a;
struct sockaddr_un au;
const struct sockaddr *sa;
int err; int err;
Actual_Socket ret; Actual_Socket ret;
short localport; short localport;
int fl; int fl, salen;
/* /*
* Create Socket structure. * Create Socket structure.
@ -439,77 +446,98 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
#ifdef IPV6 #ifdef IPV6
memset(&a6,'\0',sizeof(struct sockaddr_in6)); memset(&a6,'\0',sizeof(struct sockaddr_in6));
#endif #endif
/* Loop round trying to bind */
while (1) { /* We don't try to bind to a local address for UNIX domain sockets. (Why
int retcode; * do we bother doing the bind when localport == 0 anyway?) */
if(addr->family != AF_UNIX) {
/* Loop round trying to bind */
while (1) {
int retcode;
#ifdef IPV6 #ifdef IPV6
if (addr->family == AF_INET6) { if (addr->family == AF_INET6) {
/* XXX use getaddrinfo to get a local address? */ /* XXX use getaddrinfo to get a local address? */
a6.sin6_family = AF_INET6; a6.sin6_family = AF_INET6;
a6.sin6_addr = in6addr_any; a6.sin6_addr = in6addr_any;
a6.sin6_port = htons(localport); a6.sin6_port = htons(localport);
retcode = bind(s, (struct sockaddr *) &a6, sizeof(a6)); retcode = bind(s, (struct sockaddr *) &a6, sizeof(a6));
} else } else
#endif #endif
{ {
assert(addr->family == AF_INET); assert(addr->family == AF_INET);
a.sin_family = AF_INET; a.sin_family = AF_INET;
a.sin_addr.s_addr = htonl(INADDR_ANY); a.sin_addr.s_addr = htonl(INADDR_ANY);
a.sin_port = htons(localport); a.sin_port = htons(localport);
retcode = bind(s, (struct sockaddr *) &a, sizeof(a)); retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
}
if (retcode >= 0) {
err = 0;
break; /* done */
} else {
err = errno;
if (err != EADDRINUSE) /* failed, for a bad reason */
break;
}
if (localport == 0)
break; /* we're only looping once */
localport--;
if (localport == 0)
break; /* we might have got to the end */
} }
if (retcode >= 0) {
err = 0; if (err) {
break; /* done */ ret->error = error_string(err);
} else { return (Socket) ret;
err = errno;
if (err != EADDRINUSE) /* failed, for a bad reason */
break;
} }
if (localport == 0)
break; /* we're only looping once */
localport--;
if (localport == 0)
break; /* we might have got to the end */
}
if (err) {
ret->error = error_string(err);
return (Socket) ret;
} }
/* /*
* Connect to remote address. * Connect to remote address.
*/ */
switch(addr->family) {
#ifdef IPV6 #ifdef IPV6
/* XXX would be better to have got getaddrinfo() to fill in the port. */ case AF_INET:
if (addr->family == AF_INET) /* XXX would be better to have got getaddrinfo() to fill in the port. */
((struct sockaddr_in *)addr->ai->ai_addr)->sin_port = ((struct sockaddr_in *)addr->ai->ai_addr)->sin_port =
htons(port); htons(port);
else { sa = (const struct sockaddr *)addr->ai->ai_addr;
assert(addr->family == AF_INET6); salen = addr->ai->ai_addrlen;
break;
case AF_INET6:
((struct sockaddr_in *)addr->ai->ai_addr)->sin_port = ((struct sockaddr_in *)addr->ai->ai_addr)->sin_port =
htons(port); htons(port);
} sa = (const struct sockaddr *)addr->ai->ai_addr;
salen = addr->ai->ai_addrlen;
break;
#else #else
a.sin_family = AF_INET; case AF_INET:
a.sin_addr.s_addr = htonl(addr->address); a.sin_family = AF_INET;
a.sin_port = htons((short) port); a.sin_addr.s_addr = htonl(addr->address);
a.sin_port = htons((short) port);
sa = (const struct sockaddr *)&a;
salen = sizeof a;
break;
#endif #endif
case AF_UNIX:
assert(port == 0); /* to catch confused people */
assert(strlen(addr->hostname) < sizeof au.sun_path);
memset(&au, 0, sizeof au);
au.sun_family = AF_UNIX;
strcpy(au.sun_path, addr->hostname);
sa = (const struct sockaddr *)&au;
salen = sizeof au;
break;
default:
assert(0 && "unknown address family");
}
fl = fcntl(s, F_GETFL); fl = fcntl(s, F_GETFL);
if (fl != -1) if (fl != -1)
fcntl(s, F_SETFL, fl | O_NONBLOCK); fcntl(s, F_SETFL, fl | O_NONBLOCK);
if (( if ((connect(s, sa, salen)) < 0) {
#ifdef IPV6
connect(s, addr->ai->ai_addr, addr->ai->ai_addrlen)
#else
connect(s, (struct sockaddr *) &a, sizeof(a))
#endif
) < 0) {
if ( errno != EINPROGRESS ) { if ( errno != EINPROGRESS ) {
ret->error = error_string(errno); ret->error = error_string(errno);
return (Socket) ret; return (Socket) ret;
@ -688,19 +716,28 @@ int sk_getxdmdata(void *sock, unsigned long *ip, int *port)
if (s->fn != &tcp_fn_table) if (s->fn != &tcp_fn_table)
return 0; /* failure */ return 0; /* failure */
/*
* If we ever implement connecting to a local X server through
* a Unix socket, we return 0xFFFFFFFF for the IP address and
* our current pid for the port. Bizarre, but such is life.
*/
addrlen = sizeof(addr); addrlen = sizeof(addr);
if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 0 || if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
addr.sin_family != AF_INET)
return 0; return 0;
switch(addr.sin_family) {
case AF_INET:
*ip = ntohl(addr.sin_addr.s_addr);
*port = ntohs(addr.sin_port);
break;
case AF_UNIX:
/*
* For a Unix socket, we return 0xFFFFFFFF for the IP address and
* our current pid for the port. Bizarre, but such is life.
*/
*ip = ntohl(0xFFFFFFFF);
*port = getpid();
break;
*ip = ntohl(addr.sin_addr.s_addr); /* XXX IPV6 */
*port = ntohs(addr.sin_port);
default:
return 0;
}
return 1; return 1;
} }
@ -1062,3 +1099,21 @@ int net_service_lookup(char *service)
else else
return 0; return 0;
} }
SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname)
{
SockAddr ret = snew(struct SockAddr_tag);
int n;
memset(ret, 0, sizeof *ret);
ret->family = AF_UNIX;
n = snprintf(ret->hostname, sizeof ret->hostname,
"%s%d", X11_UNIX_PATH, displaynum);
if(n < 0)
ret->error = "snprintf failed";
else if(n >= sizeof ret->hostname)
ret->error = "X11 UNIX name too long";
else
*canonicalname = dupstr(ret->hostname);
return ret;
}

Просмотреть файл

@ -34,6 +34,8 @@ void platform_get_x11_auth(char *display, int *protocol,
/* Do nothing, therefore no auth. */ /* Do nothing, therefore no auth. */
} }
const char platform_x11_best_transport[] = "unix";
/* /*
* Default settings that are specific to PSFTP. * Default settings that are specific to PSFTP.
*/ */

Просмотреть файл

@ -14,6 +14,8 @@ void platform_get_x11_auth(char *display, int *proto,
/* We don't support this at all under Windows. */ /* We don't support this at all under Windows. */
} }
const char platform_x11_best_transport[] = "localhost";
Filename filename_from_str(const char *str) Filename filename_from_str(const char *str)
{ {
Filename ret; Filename ret;

Просмотреть файл

@ -1357,3 +1357,11 @@ int net_service_lookup(char *service)
else else
return 0; return 0;
} }
SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname)
{
SockAddr ret = snew(struct SockAddr_tag);
memset(ret, 0, sizeof(struct SockAddr_tag));
ret->error = "unix sockets not supported on this platform";
return ret;
}

Просмотреть файл

@ -226,6 +226,18 @@ int x11_get_screen_number(char *display)
return atoi(display + n + 1); return atoi(display + n + 1);
} }
/* Find the right display, returns an allocated string */
char *x11_display(const char *display) {
if(!display || !*display)
if(!(display = getenv("DISPLAY")))
display = ":0";
if(display[0] == ':') {
/* no transport specified, use whatever we think is best */
return dupcat(platform_x11_best_transport, display, (char *)0);
} else
return dupstr(display);
}
/* /*
* Called to set up the raw connection. * Called to set up the raw connection.
* *
@ -250,36 +262,39 @@ const char *x11_init(Socket * s, char *display, void *c, void *auth,
int n, displaynum; int n, displaynum;
struct X11Private *pr; struct X11Private *pr;
/* default display */
display = x11_display(display);
/* /*
* Split up display name into host and display-number parts. * Split up display name into host and display-number parts.
*/ */
n = strcspn(display, ":"); n = strcspn(display, ":");
assert(n != 0); /* x11_display() promises this */
if (display[n]) if (display[n])
displaynum = atoi(display + n + 1); displaynum = atoi(display + n + 1);
else else
displaynum = 0; /* sensible default */ displaynum = 0; /* sensible default */
if (n > sizeof(host) - 1) if (n > sizeof(host) - 1)
n = sizeof(host) - 1; n = sizeof(host) - 1;
if (n > 0) { strncpy(host, display, n);
strncpy(host, display, n); host[n] = '\0';
host[n] = '\0'; sfree(display);
if(!strcmp(host, "unix")) {
/* use AF_UNIX sockets (doesn't make sense on all platforms) */
addr = platform_get_x11_unix_address(displaynum,
&dummy_realhost);
port = 0; /* to show we are not confused */
} else { } else {
port = 6000 + displaynum;
/* /*
* Local display numbers, particularly on Unix, often omit * Try to find host.
* the display part completely.
*/ */
strcpy(host, "localhost"); addr = name_lookup(host, port, &dummy_realhost, cfg);
} if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
port = 6000 + displaynum; return err;
}
/*
* Try to find host.
*/
addr = name_lookup(host, port, &dummy_realhost, cfg);
if ((err = sk_addr_error(addr)) != NULL) {
sk_addr_free(addr);
return err;
} }
/* /*