x11disp.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * Functions to manage an X11Display structure, by creating one from
  3. * an ordinary display name string, and freeing one.
  4. */
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <assert.h>
  8. #include <time.h>
  9. #include "putty.h"
  10. #include "ssh.h"
  11. #include "ssh/channel.h"
  12. #include "tree234.h"
  13. struct X11Display *x11_setup_display(const char *display, Conf *conf,
  14. char **error_msg)
  15. {
  16. struct X11Display *disp = snew(struct X11Display);
  17. char *localcopy;
  18. *error_msg = NULL;
  19. if (!display || !*display) {
  20. localcopy = platform_get_x_display();
  21. if (!localcopy || !*localcopy) {
  22. sfree(localcopy);
  23. localcopy = dupstr(":0"); /* plausible default for any platform */
  24. }
  25. } else
  26. localcopy = dupstr(display);
  27. /*
  28. * Parse the display name.
  29. *
  30. * We expect this to have one of the following forms:
  31. *
  32. * - the standard X format which looks like
  33. * [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ]
  34. * (X11 also permits a double colon to indicate DECnet, but
  35. * that's not our problem, thankfully!)
  36. *
  37. * - only seen in the wild on MacOS (so far): a pathname to a
  38. * Unix-domain socket, which will typically and confusingly
  39. * end in ":0", and which I'm currently distinguishing from
  40. * the standard scheme by noting that it starts with '/'.
  41. */
  42. if (localcopy[0] == '/') {
  43. disp->unixsocketpath = localcopy;
  44. disp->unixdomain = true;
  45. disp->hostname = NULL;
  46. disp->displaynum = -1;
  47. disp->screennum = 0;
  48. disp->addr = NULL;
  49. } else {
  50. char *colon, *dot, *slash;
  51. char *protocol, *hostname;
  52. colon = host_strrchr(localcopy, ':');
  53. if (!colon) {
  54. *error_msg = dupprintf("display name '%s' has no ':number'"
  55. " suffix", localcopy);
  56. sfree(disp);
  57. sfree(localcopy);
  58. return NULL;
  59. }
  60. *colon++ = '\0';
  61. dot = strchr(colon, '.');
  62. if (dot)
  63. *dot++ = '\0';
  64. disp->displaynum = atoi(colon);
  65. if (dot)
  66. disp->screennum = atoi(dot);
  67. else
  68. disp->screennum = 0;
  69. protocol = NULL;
  70. hostname = localcopy;
  71. if (colon > localcopy) {
  72. slash = strchr(localcopy, '/');
  73. if (slash) {
  74. *slash++ = '\0';
  75. protocol = localcopy;
  76. hostname = slash;
  77. }
  78. }
  79. disp->hostname = *hostname ? dupstr(hostname) : NULL;
  80. if (protocol)
  81. disp->unixdomain = (!strcmp(protocol, "local") ||
  82. !strcmp(protocol, "unix"));
  83. else if (!*hostname || !strcmp(hostname, "unix"))
  84. disp->unixdomain = platform_uses_x11_unix_by_default;
  85. else
  86. disp->unixdomain = false;
  87. if (!disp->hostname && !disp->unixdomain)
  88. disp->hostname = dupstr("localhost");
  89. disp->unixsocketpath = NULL;
  90. disp->addr = NULL;
  91. sfree(localcopy);
  92. }
  93. /*
  94. * Look up the display hostname, if we need to.
  95. */
  96. if (!disp->unixdomain) {
  97. const char *err;
  98. disp->port = 6000 + disp->displaynum;
  99. disp->addr = name_lookup(disp->hostname, disp->port,
  100. &disp->realhost, conf, ADDRTYPE_UNSPEC,
  101. NULL, NULL);
  102. if ((err = sk_addr_error(disp->addr)) != NULL) {
  103. *error_msg = dupprintf("unable to resolve host name '%s' in "
  104. "display name", disp->hostname);
  105. sk_addr_free(disp->addr);
  106. sfree(disp->hostname);
  107. sfree(disp->unixsocketpath);
  108. sfree(disp);
  109. return NULL;
  110. }
  111. }
  112. /*
  113. * Try upgrading an IP-style localhost display to a Unix-socket
  114. * display (as the standard X connection libraries do).
  115. */
  116. if (!disp->unixdomain && sk_address_is_local(disp->addr)) {
  117. SockAddr *ux = platform_get_x11_unix_address(NULL, disp->displaynum);
  118. const char *err = sk_addr_error(ux);
  119. if (!err) {
  120. /* Create trial connection to see if there is a useful Unix-domain
  121. * socket */
  122. Socket *s = sk_new(sk_addr_dup(ux), 0, false, false,
  123. false, false, nullplug);
  124. err = sk_socket_error(s);
  125. sk_close(s);
  126. }
  127. if (err) {
  128. sk_addr_free(ux);
  129. } else {
  130. sk_addr_free(disp->addr);
  131. disp->unixdomain = true;
  132. disp->addr = ux;
  133. /* Fill in the rest in a moment */
  134. }
  135. }
  136. if (disp->unixdomain) {
  137. if (!disp->addr)
  138. disp->addr = platform_get_x11_unix_address(disp->unixsocketpath,
  139. disp->displaynum);
  140. if (disp->unixsocketpath)
  141. disp->realhost = dupstr(disp->unixsocketpath);
  142. else
  143. disp->realhost = dupprintf("unix:%d", disp->displaynum);
  144. disp->port = 0;
  145. }
  146. /*
  147. * Fetch the local authorisation details.
  148. */
  149. disp->localauthproto = X11_NO_AUTH;
  150. disp->localauthdata = NULL;
  151. disp->localauthdatalen = 0;
  152. platform_get_x11_auth(disp, conf);
  153. return disp;
  154. }
  155. void x11_free_display(struct X11Display *disp)
  156. {
  157. sfree(disp->hostname);
  158. sfree(disp->unixsocketpath);
  159. if (disp->localauthdata)
  160. smemclr(disp->localauthdata, disp->localauthdatalen);
  161. sfree(disp->localauthdata);
  162. sk_addr_free(disp->addr);
  163. sfree(disp);
  164. }