From a2acc6ae0d9f3afe72e85e583ed50c75479e787a Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 31 May 2004 14:01:52 +0000 Subject: [PATCH] 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] --- settings.c | 2 +- ssh.h | 7 +- unix/ux_x11.c | 12 +++- unix/uxnet.c | 175 +++++++++++++++++++++++++++++++++----------------- unix/uxsftp.c | 2 + winmisc.c | 2 + winnet.c | 8 +++ x11fwd.c | 49 +++++++++----- 8 files changed, 176 insertions(+), 81 deletions(-) diff --git a/settings.c b/settings.c index b9e6621f..99ec7eeb 100644 --- a/settings.c +++ b/settings.c @@ -611,7 +611,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg) gppi(sesskey, "BCE", 1, &cfg->bce); gppi(sesskey, "BlinkText", 0, &cfg->blinktext); 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)); gppi(sesskey, "X11AuthType", X11_MIT, &cfg->x11_auth); diff --git a/ssh.h b/ssh.h index 90f77d10..07cdc7ef 100644 --- a/ssh.h +++ b/ssh.h @@ -281,10 +281,15 @@ extern void x11_unthrottle(Socket s); extern void x11_override_throttle(Socket s, int enable); extern int x11_get_screen_number(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, 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 bn_power_2(int n); diff --git a/unix/ux_x11.c b/unix/ux_x11.c index 67c76f83..1c48b141 100644 --- a/unix/ux_x11.c +++ b/unix/ux_x11.c @@ -4,7 +4,9 @@ #include #include +#include #include "putty.h" +#include "ssh.h" void platform_get_x11_auth(char *display, int *protocol, unsigned char *data, int *datalen) @@ -15,15 +17,19 @@ void platform_get_x11_auth(char *display, int *protocol, char *localbuf; int proto = -1; + display = x11_display(display); /* * Normally we should run `xauth list DISPLAYNAME'. However, * there's an oddity when the display is local: the display * `localhost:0' (or `:0') should become just `:0'. */ - if (!strncmp(display, "localhost:", 10)) - command = dupprintf("xauth list %s 2>/dev/null", display+9); + if (!strncmp(display, "localhost:", 10) + || !strncmp(display, "unix:", 5)) + command = dupprintf("xauth list %s 2>/dev/null", + strchr(display, ':')); else command = dupprintf("xauth list %s 2>/dev/null", display); + sfree(display); fp = popen(command, "r"); sfree(command); @@ -113,3 +119,5 @@ void platform_get_x11_auth(char *display, int *protocol, pclose(fp); sfree(localbuf); } + +const char platform_x11_best_transport[] = "unix"; diff --git a/unix/uxnet.c b/unix/uxnet.c index 46b619bd..1e7d51b7 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -15,12 +15,17 @@ #include #include #include +#include #define DEFINE_PLUG_METHOD_MACROS #include "putty.h" #include "network.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) struct Socket_tag { @@ -380,10 +385,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, struct sockaddr_in6 a6; #endif struct sockaddr_in a; + struct sockaddr_un au; + const struct sockaddr *sa; int err; Actual_Socket ret; short localport; - int fl; + int fl, salen; /* * Create Socket structure. @@ -439,77 +446,98 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, #ifdef IPV6 memset(&a6,'\0',sizeof(struct sockaddr_in6)); #endif - /* Loop round trying to bind */ - while (1) { - int retcode; + + /* We don't try to bind to a local address for UNIX domain sockets. (Why + * 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 - if (addr->family == AF_INET6) { - /* XXX use getaddrinfo to get a local address? */ - a6.sin6_family = AF_INET6; - a6.sin6_addr = in6addr_any; - a6.sin6_port = htons(localport); - retcode = bind(s, (struct sockaddr *) &a6, sizeof(a6)); - } else + if (addr->family == AF_INET6) { + /* XXX use getaddrinfo to get a local address? */ + a6.sin6_family = AF_INET6; + a6.sin6_addr = in6addr_any; + a6.sin6_port = htons(localport); + retcode = bind(s, (struct sockaddr *) &a6, sizeof(a6)); + } else #endif - { - assert(addr->family == AF_INET); - a.sin_family = AF_INET; - a.sin_addr.s_addr = htonl(INADDR_ANY); - a.sin_port = htons(localport); - retcode = bind(s, (struct sockaddr *) &a, sizeof(a)); + { + assert(addr->family == AF_INET); + a.sin_family = AF_INET; + a.sin_addr.s_addr = htonl(INADDR_ANY); + a.sin_port = htons(localport); + 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; - break; /* done */ - } else { - err = errno; - if (err != EADDRINUSE) /* failed, for a bad reason */ - break; + + if (err) { + ret->error = error_string(err); + return (Socket) ret; } - - 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. */ + switch(addr->family) { #ifdef IPV6 - /* XXX would be better to have got getaddrinfo() to fill in the port. */ - if (addr->family == AF_INET) + 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 = htons(port); - else { - assert(addr->family == AF_INET6); + sa = (const struct sockaddr *)addr->ai->ai_addr; + salen = addr->ai->ai_addrlen; + break; + case AF_INET6: ((struct sockaddr_in *)addr->ai->ai_addr)->sin_port = htons(port); - } + sa = (const struct sockaddr *)addr->ai->ai_addr; + salen = addr->ai->ai_addrlen; + break; #else - a.sin_family = AF_INET; - a.sin_addr.s_addr = htonl(addr->address); - a.sin_port = htons((short) port); + case AF_INET: + a.sin_family = AF_INET; + a.sin_addr.s_addr = htonl(addr->address); + a.sin_port = htons((short) port); + sa = (const struct sockaddr *)&a; + salen = sizeof a; + break; #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); if (fl != -1) fcntl(s, F_SETFL, fl | O_NONBLOCK); - if (( -#ifdef IPV6 - connect(s, addr->ai->ai_addr, addr->ai->ai_addrlen) -#else - connect(s, (struct sockaddr *) &a, sizeof(a)) -#endif - ) < 0) { + if ((connect(s, sa, salen)) < 0) { if ( errno != EINPROGRESS ) { ret->error = error_string(errno); return (Socket) ret; @@ -688,19 +716,28 @@ int sk_getxdmdata(void *sock, unsigned long *ip, int *port) if (s->fn != &tcp_fn_table) 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); - if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 0 || - addr.sin_family != AF_INET) + if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 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); - *port = ntohs(addr.sin_port); + /* XXX IPV6 */ + + default: + return 0; + } return 1; } @@ -1062,3 +1099,21 @@ int net_service_lookup(char *service) else 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; +} diff --git a/unix/uxsftp.c b/unix/uxsftp.c index c86b8ea7..6e1a0edc 100644 --- a/unix/uxsftp.c +++ b/unix/uxsftp.c @@ -34,6 +34,8 @@ void platform_get_x11_auth(char *display, int *protocol, /* Do nothing, therefore no auth. */ } +const char platform_x11_best_transport[] = "unix"; + /* * Default settings that are specific to PSFTP. */ diff --git a/winmisc.c b/winmisc.c index 2357ea6d..71f09a87 100644 --- a/winmisc.c +++ b/winmisc.c @@ -14,6 +14,8 @@ void platform_get_x11_auth(char *display, int *proto, /* We don't support this at all under Windows. */ } +const char platform_x11_best_transport[] = "localhost"; + Filename filename_from_str(const char *str) { Filename ret; diff --git a/winnet.c b/winnet.c index 8be2cd17..21a2f293 100644 --- a/winnet.c +++ b/winnet.c @@ -1357,3 +1357,11 @@ int net_service_lookup(char *service) else 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; +} diff --git a/x11fwd.c b/x11fwd.c index 8b045f31..049381b4 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -226,6 +226,18 @@ int x11_get_screen_number(char *display) 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. * @@ -250,36 +262,39 @@ const char *x11_init(Socket * s, char *display, void *c, void *auth, int n, displaynum; struct X11Private *pr; + /* default display */ + display = x11_display(display); /* * Split up display name into host and display-number parts. */ n = strcspn(display, ":"); + assert(n != 0); /* x11_display() promises this */ if (display[n]) displaynum = atoi(display + n + 1); else displaynum = 0; /* sensible default */ if (n > sizeof(host) - 1) n = sizeof(host) - 1; - if (n > 0) { - strncpy(host, display, n); - host[n] = '\0'; + strncpy(host, display, n); + 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 { + port = 6000 + displaynum; + /* - * Local display numbers, particularly on Unix, often omit - * the display part completely. + * Try to find host. */ - strcpy(host, "localhost"); - } - - port = 6000 + displaynum; - - /* - * 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; + addr = name_lookup(host, port, &dummy_realhost, cfg); + if ((err = sk_addr_error(addr)) != NULL) { + sk_addr_free(addr); + return err; + } } /*