2 package com.trilead.ssh2.channel;
\r
4 import java.io.IOException;
\r
5 import java.io.InputStream;
\r
6 import java.io.OutputStream;
\r
7 import java.net.Socket;
\r
9 import com.trilead.ssh2.log.Logger;
\r
13 * RemoteX11AcceptThread.
\r
15 * @author Christian Plattner, plattner@trilead.com
\r
16 * @version $Id: RemoteX11AcceptThread.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
\r
18 public class RemoteX11AcceptThread extends Thread
\r
20 private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
\r
24 String remoteOriginatorAddress;
\r
25 int remoteOriginatorPort;
\r
29 public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort)
\r
32 this.remoteOriginatorAddress = remoteOriginatorAddress;
\r
33 this.remoteOriginatorPort = remoteOriginatorPort;
\r
40 /* Send Open Confirmation */
\r
42 c.cm.sendOpenConfirmation(c);
\r
44 /* Read startup packet from client */
\r
46 OutputStream remote_os = c.getStdinStream();
\r
47 InputStream remote_is = c.getStdoutStream();
\r
49 /* The following code is based on the protocol description given in:
\r
51 * X Windows System: Core and Extension Protocols:
\r
52 * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
\r
58 * 1 0X42 MSB first/0x6c lSB first - byteorder
\r
60 * 2 card16 - protocol-major-version
\r
61 * 2 card16 - protocol-minor-version
\r
62 * 2 n - lenght of authorization-protocol-name
\r
63 * 2 d - lenght of authorization-protocol-data
\r
65 * string8 - authorization-protocol-name
\r
66 * p - unused, p=pad(n)
\r
67 * string8 - authorization-protocol-data
\r
68 * q - unused, q=pad(d)
\r
70 * pad(X) = (4 - (X mod 4)) mod 4
\r
74 * 1 (0 failed, 2 authenticate, 1 success)
\r
79 /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
\r
81 byte[] header = new byte[6];
\r
83 if (remote_is.read(header) != 6)
\r
84 throw new IOException("Unexpected EOF on X11 startup!");
\r
86 if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
\r
87 throw new IOException("Unknown endian format in X11 message!");
\r
89 /* Yes, I came up with this myself - shall I file an application for a patent? =) */
\r
91 int idxMSB = (header[0] == 0x42) ? 0 : 1;
\r
93 /* Read authorization data header */
\r
95 byte[] auth_buff = new byte[6];
\r
97 if (remote_is.read(auth_buff) != 6)
\r
98 throw new IOException("Unexpected EOF on X11 startup!");
\r
100 int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
\r
101 int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
\r
103 if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
\r
104 throw new IOException("Buggy X11 authorization data");
\r
106 int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
\r
107 int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
\r
109 byte[] authProtocolName = new byte[authProtocolNameLength];
\r
110 byte[] authProtocolData = new byte[authProtocolDataLength];
\r
112 byte[] paddingBuffer = new byte[4];
\r
114 if (remote_is.read(authProtocolName) != authProtocolNameLength)
\r
115 throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
\r
117 if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
\r
118 throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
\r
120 if (remote_is.read(authProtocolData) != authProtocolDataLength)
\r
121 throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
\r
123 if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
\r
124 throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
\r
126 if ("MIT-MAGIC-COOKIE-1".equals(new String(authProtocolName, "ISO-8859-1")) == false)
\r
127 throw new IOException("Unknown X11 authorization protocol!");
\r
129 if (authProtocolDataLength != 16)
\r
130 throw new IOException("Wrong data length for X11 authorization data!");
\r
132 StringBuffer tmp = new StringBuffer(32);
\r
133 for (int i = 0; i < authProtocolData.length; i++)
\r
135 String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
\r
136 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
\r
138 String hexEncodedFakeCookie = tmp.toString();
\r
140 /* Order is very important here - it may be that a certain x11 forwarding
\r
141 * gets disabled right in the moment when we check and register our connection
\r
146 /* Please read the comment in Channel.java */
\r
147 c.hexX11FakeCookie = hexEncodedFakeCookie;
\r
150 /* Now check our fake cookie directory to see if we produced this cookie */
\r
152 X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
\r
155 throw new IOException("Invalid X11 cookie received.");
\r
157 /* If the session which corresponds to this cookie is closed then we will
\r
158 * detect this: the session's close code will close all channels
\r
159 * with the session's assigned x11 fake cookie.
\r
162 s = new Socket(sd.hostname, sd.port);
\r
164 OutputStream x11_os = s.getOutputStream();
\r
165 InputStream x11_is = s.getInputStream();
\r
167 /* Now we are sending the startup packet to the real X11 server */
\r
169 x11_os.write(header);
\r
171 if (sd.x11_magic_cookie == null)
\r
173 byte[] emptyAuthData = new byte[6];
\r
174 /* empty auth data, hopefully you are connecting to localhost =) */
\r
175 x11_os.write(emptyAuthData);
\r
179 if (sd.x11_magic_cookie.length != 16)
\r
180 throw new IOException("The real X11 cookie has an invalid length!");
\r
182 /* send X11 cookie specified by client */
\r
183 x11_os.write(auth_buff);
\r
184 x11_os.write(authProtocolName); /* re-use */
\r
185 x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
\r
186 x11_os.write(sd.x11_magic_cookie);
\r
187 x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
\r
192 /* Start forwarding traffic */
\r
194 StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11");
\r
195 StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
\r
197 /* No need to start two threads, one can be executed in the current thread */
\r
199 r2l.setDaemon(true);
\r
203 while (r2l.isAlive())
\r
209 catch (InterruptedException e)
\r
214 /* If the channel is already closed, then this is a no-op */
\r
216 c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
\r
219 catch (IOException e)
\r
221 log.log(50, "IOException in X11 proxy code: " + e.getMessage());
\r
225 c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true);
\r
227 catch (IOException e1)
\r
235 catch (IOException e1)
\r