/src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
Java | 244 lines | 125 code | 55 blank | 64 comment | 35 complexity | d6e0167c5029a91322e91a7907b96dbc MD5 | raw file
Possible License(s): GPL-3.0
- package com.trilead.ssh2.channel;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.Socket;
-
- import com.trilead.ssh2.log.Logger;
-
- /**
- * RemoteX11AcceptThread.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne
- * Exp $
- */
- public class RemoteX11AcceptThread extends Thread {
- private static final Logger log = Logger
- .getLogger(RemoteX11AcceptThread.class);
-
- Channel c;
-
- String remoteOriginatorAddress;
- int remoteOriginatorPort;
-
- Socket s;
-
- public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress,
- int remoteOriginatorPort) {
- this.c = c;
- this.remoteOriginatorAddress = remoteOriginatorAddress;
- this.remoteOriginatorPort = remoteOriginatorPort;
- }
-
- @Override
- public void run() {
- try {
- /* Send Open Confirmation */
-
- c.cm.sendOpenConfirmation(c);
-
- /* Read startup packet from client */
-
- OutputStream remote_os = c.getStdinStream();
- InputStream remote_is = c.getStdoutStream();
-
- /*
- * The following code is based on the protocol description given in:
- * Scheifler/Gettys, X Windows System: Core and Extension Protocols:
- * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
- */
-
- /*
- * Client startup:
- *
- * 1 0X42 MSB first/0x6c lSB first - byteorder 1 - unused 2 card16 -
- * protocol-major-version 2 card16 - protocol-minor-version 2 n -
- * lenght of authorization-protocol-name 2 d - lenght of
- * authorization-protocol-data 2 - unused string8 -
- * authorization-protocol-name p - unused, p=pad(n) string8 -
- * authorization-protocol-data q - unused, q=pad(d)
- *
- * pad(X) = (4 - (X mod 4)) mod 4
- *
- * Server response:
- *
- * 1 (0 failed, 2 authenticate, 1 success) ...
- */
-
- /*
- * Later on we will simply forward the first 6 header bytes to the
- * "real" X11 server
- */
-
- byte[] header = new byte[6];
-
- if (remote_is.read(header) != 6)
- throw new IOException("Unexpected EOF on X11 startup!");
-
- if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first,
- // 0x6C LSB first
- throw new IOException("Unknown endian format in X11 message!");
-
- /*
- * Yes, I came up with this myself - shall I file an application for
- * a patent? =)
- */
-
- int idxMSB = (header[0] == 0x42) ? 0 : 1;
-
- /* Read authorization data header */
-
- byte[] auth_buff = new byte[6];
-
- if (remote_is.read(auth_buff) != 6)
- throw new IOException("Unexpected EOF on X11 startup!");
-
- int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8)
- | (auth_buff[1 - idxMSB] & 0xff);
- int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8)
- | (auth_buff[3 - idxMSB] & 0xff);
-
- if ((authProtocolNameLength > 256)
- || (authProtocolDataLength > 256))
- throw new IOException("Buggy X11 authorization data");
-
- int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
- int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
-
- byte[] authProtocolName = new byte[authProtocolNameLength];
- byte[] authProtocolData = new byte[authProtocolDataLength];
-
- byte[] paddingBuffer = new byte[4];
-
- if (remote_is.read(authProtocolName) != authProtocolNameLength)
- throw new IOException(
- "Unexpected EOF on X11 startup! (authProtocolName)");
-
- if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
- throw new IOException(
- "Unexpected EOF on X11 startup! (authProtocolNamePadding)");
-
- if (remote_is.read(authProtocolData) != authProtocolDataLength)
- throw new IOException(
- "Unexpected EOF on X11 startup! (authProtocolData)");
-
- if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
- throw new IOException(
- "Unexpected EOF on X11 startup! (authProtocolDataPadding)");
-
- if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName,
- "ISO-8859-1")) == false)
- throw new IOException("Unknown X11 authorization protocol!");
-
- if (authProtocolDataLength != 16)
- throw new IOException(
- "Wrong data length for X11 authorization data!");
-
- StringBuffer tmp = new StringBuffer(32);
- for (int i = 0; i < authProtocolData.length; i++) {
- String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
- tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
- }
- String hexEncodedFakeCookie = tmp.toString();
-
- /*
- * Order is very important here - it may be that a certain x11
- * forwarding gets disabled right in the moment when we check and
- * register our connection
- */
-
- synchronized (c) {
- /* Please read the comment in Channel.java */
- c.hexX11FakeCookie = hexEncodedFakeCookie;
- }
-
- /*
- * Now check our fake cookie directory to see if we produced this
- * cookie
- */
-
- X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
-
- if (sd == null)
- throw new IOException("Invalid X11 cookie received.");
-
- /*
- * If the session which corresponds to this cookie is closed then we
- * will detect this: the session's close code will close all
- * channels with the session's assigned x11 fake cookie.
- */
-
- s = new Socket(sd.hostname, sd.port);
-
- OutputStream x11_os = s.getOutputStream();
- InputStream x11_is = s.getInputStream();
-
- /* Now we are sending the startup packet to the real X11 server */
-
- x11_os.write(header);
-
- if (sd.x11_magic_cookie == null) {
- byte[] emptyAuthData = new byte[6];
- /* empty auth data, hopefully you are connecting to localhost =) */
- x11_os.write(emptyAuthData);
- } else {
- if (sd.x11_magic_cookie.length != 16)
- throw new IOException(
- "The real X11 cookie has an invalid length!");
-
- /* send X11 cookie specified by client */
- x11_os.write(auth_buff);
- x11_os.write(authProtocolName); /* re-use */
- x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
- x11_os.write(sd.x11_magic_cookie);
- x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
- }
-
- x11_os.flush();
-
- /* Start forwarding traffic */
-
- StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is,
- x11_os, "RemoteToX11");
- StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is,
- remote_os, "X11ToRemote");
-
- /*
- * No need to start two threads, one can be executed in the current
- * thread
- */
-
- r2l.setDaemon(true);
- r2l.start();
- l2r.run();
-
- while (r2l.isAlive()) {
- try {
- r2l.join();
- } catch (InterruptedException e) {
- }
- }
-
- /* If the channel is already closed, then this is a no-op */
-
- c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
- s.close();
- } catch (IOException e) {
- log.log(50, "IOException in X11 proxy code: " + e.getMessage());
-
- try {
- c.cm.closeChannel(c,
- "IOException in X11 proxy code (" + e.getMessage()
- + ")", true);
- } catch (IOException e1) {
- }
- try {
- if (s != null)
- s.close();
- } catch (IOException e1) {
- }
- }
- }
- }