PageRenderTime 43ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java

https://code.google.com/p/sshtunnel/
Java | 244 lines | 125 code | 55 blank | 64 comment | 35 complexity | d6e0167c5029a91322e91a7907b96dbc MD5 | raw file
Possible License(s): GPL-3.0
  1. package com.trilead.ssh2.channel;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.Socket;
  6. import com.trilead.ssh2.log.Logger;
  7. /**
  8. * RemoteX11AcceptThread.
  9. *
  10. * @author Christian Plattner, plattner@trilead.com
  11. * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne
  12. * Exp $
  13. */
  14. public class RemoteX11AcceptThread extends Thread {
  15. private static final Logger log = Logger
  16. .getLogger(RemoteX11AcceptThread.class);
  17. Channel c;
  18. String remoteOriginatorAddress;
  19. int remoteOriginatorPort;
  20. Socket s;
  21. public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress,
  22. int remoteOriginatorPort) {
  23. this.c = c;
  24. this.remoteOriginatorAddress = remoteOriginatorAddress;
  25. this.remoteOriginatorPort = remoteOriginatorPort;
  26. }
  27. @Override
  28. public void run() {
  29. try {
  30. /* Send Open Confirmation */
  31. c.cm.sendOpenConfirmation(c);
  32. /* Read startup packet from client */
  33. OutputStream remote_os = c.getStdinStream();
  34. InputStream remote_is = c.getStdoutStream();
  35. /*
  36. * The following code is based on the protocol description given in:
  37. * Scheifler/Gettys, X Windows System: Core and Extension Protocols:
  38. * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
  39. */
  40. /*
  41. * Client startup:
  42. *
  43. * 1 0X42 MSB first/0x6c lSB first - byteorder 1 - unused 2 card16 -
  44. * protocol-major-version 2 card16 - protocol-minor-version 2 n -
  45. * lenght of authorization-protocol-name 2 d - lenght of
  46. * authorization-protocol-data 2 - unused string8 -
  47. * authorization-protocol-name p - unused, p=pad(n) string8 -
  48. * authorization-protocol-data q - unused, q=pad(d)
  49. *
  50. * pad(X) = (4 - (X mod 4)) mod 4
  51. *
  52. * Server response:
  53. *
  54. * 1 (0 failed, 2 authenticate, 1 success) ...
  55. */
  56. /*
  57. * Later on we will simply forward the first 6 header bytes to the
  58. * "real" X11 server
  59. */
  60. byte[] header = new byte[6];
  61. if (remote_is.read(header) != 6)
  62. throw new IOException("Unexpected EOF on X11 startup!");
  63. if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first,
  64. // 0x6C LSB first
  65. throw new IOException("Unknown endian format in X11 message!");
  66. /*
  67. * Yes, I came up with this myself - shall I file an application for
  68. * a patent? =)
  69. */
  70. int idxMSB = (header[0] == 0x42) ? 0 : 1;
  71. /* Read authorization data header */
  72. byte[] auth_buff = new byte[6];
  73. if (remote_is.read(auth_buff) != 6)
  74. throw new IOException("Unexpected EOF on X11 startup!");
  75. int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8)
  76. | (auth_buff[1 - idxMSB] & 0xff);
  77. int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8)
  78. | (auth_buff[3 - idxMSB] & 0xff);
  79. if ((authProtocolNameLength > 256)
  80. || (authProtocolDataLength > 256))
  81. throw new IOException("Buggy X11 authorization data");
  82. int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
  83. int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
  84. byte[] authProtocolName = new byte[authProtocolNameLength];
  85. byte[] authProtocolData = new byte[authProtocolDataLength];
  86. byte[] paddingBuffer = new byte[4];
  87. if (remote_is.read(authProtocolName) != authProtocolNameLength)
  88. throw new IOException(
  89. "Unexpected EOF on X11 startup! (authProtocolName)");
  90. if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
  91. throw new IOException(
  92. "Unexpected EOF on X11 startup! (authProtocolNamePadding)");
  93. if (remote_is.read(authProtocolData) != authProtocolDataLength)
  94. throw new IOException(
  95. "Unexpected EOF on X11 startup! (authProtocolData)");
  96. if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
  97. throw new IOException(
  98. "Unexpected EOF on X11 startup! (authProtocolDataPadding)");
  99. if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName,
  100. "ISO-8859-1")) == false)
  101. throw new IOException("Unknown X11 authorization protocol!");
  102. if (authProtocolDataLength != 16)
  103. throw new IOException(
  104. "Wrong data length for X11 authorization data!");
  105. StringBuffer tmp = new StringBuffer(32);
  106. for (int i = 0; i < authProtocolData.length; i++) {
  107. String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
  108. tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
  109. }
  110. String hexEncodedFakeCookie = tmp.toString();
  111. /*
  112. * Order is very important here - it may be that a certain x11
  113. * forwarding gets disabled right in the moment when we check and
  114. * register our connection
  115. */
  116. synchronized (c) {
  117. /* Please read the comment in Channel.java */
  118. c.hexX11FakeCookie = hexEncodedFakeCookie;
  119. }
  120. /*
  121. * Now check our fake cookie directory to see if we produced this
  122. * cookie
  123. */
  124. X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
  125. if (sd == null)
  126. throw new IOException("Invalid X11 cookie received.");
  127. /*
  128. * If the session which corresponds to this cookie is closed then we
  129. * will detect this: the session's close code will close all
  130. * channels with the session's assigned x11 fake cookie.
  131. */
  132. s = new Socket(sd.hostname, sd.port);
  133. OutputStream x11_os = s.getOutputStream();
  134. InputStream x11_is = s.getInputStream();
  135. /* Now we are sending the startup packet to the real X11 server */
  136. x11_os.write(header);
  137. if (sd.x11_magic_cookie == null) {
  138. byte[] emptyAuthData = new byte[6];
  139. /* empty auth data, hopefully you are connecting to localhost =) */
  140. x11_os.write(emptyAuthData);
  141. } else {
  142. if (sd.x11_magic_cookie.length != 16)
  143. throw new IOException(
  144. "The real X11 cookie has an invalid length!");
  145. /* send X11 cookie specified by client */
  146. x11_os.write(auth_buff);
  147. x11_os.write(authProtocolName); /* re-use */
  148. x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
  149. x11_os.write(sd.x11_magic_cookie);
  150. x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
  151. }
  152. x11_os.flush();
  153. /* Start forwarding traffic */
  154. StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is,
  155. x11_os, "RemoteToX11");
  156. StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is,
  157. remote_os, "X11ToRemote");
  158. /*
  159. * No need to start two threads, one can be executed in the current
  160. * thread
  161. */
  162. r2l.setDaemon(true);
  163. r2l.start();
  164. l2r.run();
  165. while (r2l.isAlive()) {
  166. try {
  167. r2l.join();
  168. } catch (InterruptedException e) {
  169. }
  170. }
  171. /* If the channel is already closed, then this is a no-op */
  172. c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
  173. s.close();
  174. } catch (IOException e) {
  175. log.log(50, "IOException in X11 proxy code: " + e.getMessage());
  176. try {
  177. c.cm.closeChannel(c,
  178. "IOException in X11 proxy code (" + e.getMessage()
  179. + ")", true);
  180. } catch (IOException e1) {
  181. }
  182. try {
  183. if (s != null)
  184. s.close();
  185. } catch (IOException e1) {
  186. }
  187. }
  188. }
  189. }