view src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java @ 245:23119f3c0d28
add host preference for fixed screen size
author
Carl Byington <carl@five-ten-sg.com>
date
Mon, 14 Jul 2014 11:15:42 -0700 (2014-07-14)
parents
0ce5cc452d02
children
line source
+ −
+ − 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;
+ − }
+ −
+ − 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, s, 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) {
+ − }
+ − }
+ − }
+ − }