mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
190 lines
5.5 KiB
C
190 lines
5.5 KiB
C
|
/*
|
||
|
* Functions to manage an X11Display structure, by creating one from
|
||
|
* an ordinary display name string, and freeing one.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "putty.h"
|
||
|
#include "ssh.h"
|
||
|
#include "sshchan.h"
|
||
|
#include "tree234.h"
|
||
|
|
||
|
struct X11Display *x11_setup_display(const char *display, Conf *conf,
|
||
|
char **error_msg)
|
||
|
{
|
||
|
struct X11Display *disp = snew(struct X11Display);
|
||
|
char *localcopy;
|
||
|
|
||
|
*error_msg = NULL;
|
||
|
|
||
|
if (!display || !*display) {
|
||
|
localcopy = platform_get_x_display();
|
||
|
if (!localcopy || !*localcopy) {
|
||
|
sfree(localcopy);
|
||
|
localcopy = dupstr(":0"); /* plausible default for any platform */
|
||
|
}
|
||
|
} else
|
||
|
localcopy = dupstr(display);
|
||
|
|
||
|
/*
|
||
|
* Parse the display name.
|
||
|
*
|
||
|
* We expect this to have one of the following forms:
|
||
|
*
|
||
|
* - the standard X format which looks like
|
||
|
* [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ]
|
||
|
* (X11 also permits a double colon to indicate DECnet, but
|
||
|
* that's not our problem, thankfully!)
|
||
|
*
|
||
|
* - only seen in the wild on MacOS (so far): a pathname to a
|
||
|
* Unix-domain socket, which will typically and confusingly
|
||
|
* end in ":0", and which I'm currently distinguishing from
|
||
|
* the standard scheme by noting that it starts with '/'.
|
||
|
*/
|
||
|
if (localcopy[0] == '/') {
|
||
|
disp->unixsocketpath = localcopy;
|
||
|
disp->unixdomain = true;
|
||
|
disp->hostname = NULL;
|
||
|
disp->displaynum = -1;
|
||
|
disp->screennum = 0;
|
||
|
disp->addr = NULL;
|
||
|
} else {
|
||
|
char *colon, *dot, *slash;
|
||
|
char *protocol, *hostname;
|
||
|
|
||
|
colon = host_strrchr(localcopy, ':');
|
||
|
if (!colon) {
|
||
|
*error_msg = dupprintf("display name '%s' has no ':number'"
|
||
|
" suffix", localcopy);
|
||
|
|
||
|
sfree(disp);
|
||
|
sfree(localcopy);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
*colon++ = '\0';
|
||
|
dot = strchr(colon, '.');
|
||
|
if (dot)
|
||
|
*dot++ = '\0';
|
||
|
|
||
|
disp->displaynum = atoi(colon);
|
||
|
if (dot)
|
||
|
disp->screennum = atoi(dot);
|
||
|
else
|
||
|
disp->screennum = 0;
|
||
|
|
||
|
protocol = NULL;
|
||
|
hostname = localcopy;
|
||
|
if (colon > localcopy) {
|
||
|
slash = strchr(localcopy, '/');
|
||
|
if (slash) {
|
||
|
*slash++ = '\0';
|
||
|
protocol = localcopy;
|
||
|
hostname = slash;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
disp->hostname = *hostname ? dupstr(hostname) : NULL;
|
||
|
|
||
|
if (protocol)
|
||
|
disp->unixdomain = (!strcmp(protocol, "local") ||
|
||
|
!strcmp(protocol, "unix"));
|
||
|
else if (!*hostname || !strcmp(hostname, "unix"))
|
||
|
disp->unixdomain = platform_uses_x11_unix_by_default;
|
||
|
else
|
||
|
disp->unixdomain = false;
|
||
|
|
||
|
if (!disp->hostname && !disp->unixdomain)
|
||
|
disp->hostname = dupstr("localhost");
|
||
|
|
||
|
disp->unixsocketpath = NULL;
|
||
|
disp->addr = NULL;
|
||
|
|
||
|
sfree(localcopy);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Look up the display hostname, if we need to.
|
||
|
*/
|
||
|
if (!disp->unixdomain) {
|
||
|
const char *err;
|
||
|
|
||
|
disp->port = 6000 + disp->displaynum;
|
||
|
disp->addr = name_lookup(disp->hostname, disp->port,
|
||
|
&disp->realhost, conf, ADDRTYPE_UNSPEC,
|
||
|
NULL, NULL);
|
||
|
|
||
|
if ((err = sk_addr_error(disp->addr)) != NULL) {
|
||
|
*error_msg = dupprintf("unable to resolve host name '%s' in "
|
||
|
"display name", disp->hostname);
|
||
|
|
||
|
sk_addr_free(disp->addr);
|
||
|
sfree(disp->hostname);
|
||
|
sfree(disp->unixsocketpath);
|
||
|
sfree(disp);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Try upgrading an IP-style localhost display to a Unix-socket
|
||
|
* display (as the standard X connection libraries do).
|
||
|
*/
|
||
|
if (!disp->unixdomain && sk_address_is_local(disp->addr)) {
|
||
|
SockAddr *ux = platform_get_x11_unix_address(NULL, disp->displaynum);
|
||
|
const char *err = sk_addr_error(ux);
|
||
|
if (!err) {
|
||
|
/* Create trial connection to see if there is a useful Unix-domain
|
||
|
* socket */
|
||
|
Socket *s = sk_new(sk_addr_dup(ux), 0, false, false,
|
||
|
false, false, nullplug);
|
||
|
err = sk_socket_error(s);
|
||
|
sk_close(s);
|
||
|
}
|
||
|
if (err) {
|
||
|
sk_addr_free(ux);
|
||
|
} else {
|
||
|
sk_addr_free(disp->addr);
|
||
|
disp->unixdomain = true;
|
||
|
disp->addr = ux;
|
||
|
/* Fill in the rest in a moment */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (disp->unixdomain) {
|
||
|
if (!disp->addr)
|
||
|
disp->addr = platform_get_x11_unix_address(disp->unixsocketpath,
|
||
|
disp->displaynum);
|
||
|
if (disp->unixsocketpath)
|
||
|
disp->realhost = dupstr(disp->unixsocketpath);
|
||
|
else
|
||
|
disp->realhost = dupprintf("unix:%d", disp->displaynum);
|
||
|
disp->port = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fetch the local authorisation details.
|
||
|
*/
|
||
|
disp->localauthproto = X11_NO_AUTH;
|
||
|
disp->localauthdata = NULL;
|
||
|
disp->localauthdatalen = 0;
|
||
|
platform_get_x11_auth(disp, conf);
|
||
|
|
||
|
return disp;
|
||
|
}
|
||
|
|
||
|
void x11_free_display(struct X11Display *disp)
|
||
|
{
|
||
|
sfree(disp->hostname);
|
||
|
sfree(disp->unixsocketpath);
|
||
|
if (disp->localauthdata)
|
||
|
smemclr(disp->localauthdata, disp->localauthdatalen);
|
||
|
sfree(disp->localauthdata);
|
||
|
sk_addr_free(disp->addr);
|
||
|
sfree(disp);
|
||
|
}
|