001: package ch.ethz.ssh2.channel;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.io.OutputStream;
006: import java.net.Socket;
007:
008: import ch.ethz.ssh2.log.Logger;
009:
010: /**
011: * RemoteX11AcceptThread.
012: *
013: * @author Christian Plattner, plattner@inf.ethz.ch
014: * @version $Id: RemoteX11AcceptThread.java,v 1.5 2006/02/14 15:17:37 cplattne Exp $
015: */
016: public class RemoteX11AcceptThread extends Thread {
017: private static final Logger log = Logger
018: .getLogger(RemoteX11AcceptThread.class);
019:
020: Channel c;
021:
022: String remoteOriginatorAddress;
023: int remoteOriginatorPort;
024:
025: Socket s;
026:
027: public RemoteX11AcceptThread(Channel c,
028: String remoteOriginatorAddress, int remoteOriginatorPort) {
029: this .c = c;
030: this .remoteOriginatorAddress = remoteOriginatorAddress;
031: this .remoteOriginatorPort = remoteOriginatorPort;
032: }
033:
034: public void run() {
035: try {
036: /* Send Open Confirmation */
037:
038: c.cm.sendOpenConfirmation(c);
039:
040: /* Read startup packet from client */
041:
042: OutputStream remote_os = c.getStdinStream();
043: InputStream remote_is = c.getStdoutStream();
044:
045: /* The following code is based on the protocol description given in:
046: * Scheifler/Gettys,
047: * X Windows System: Core and Extension Protocols:
048: * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
049: * (from the ETH library - after being here for almost ten
050: * years one of the few books I borrowed... sad but true =)
051: */
052:
053: /*
054: * Client startup:
055: *
056: * 1 0X42 MSB first/0x6c lSB first - byteorder
057: * 1 - unused
058: * 2 card16 - protocol-major-version
059: * 2 card16 - protocol-minor-version
060: * 2 n - lenght of authorization-protocol-name
061: * 2 d - lenght of authorization-protocol-data
062: * 2 - unused
063: * string8 - authorization-protocol-name
064: * p - unused, p=pad(n)
065: * string8 - authorization-protocol-data
066: * q - unused, q=pad(d)
067: *
068: * pad(X) = (4 - (X mod 4)) mod 4
069: *
070: * Server response:
071: *
072: * 1 (0 failed, 2 authenticate, 1 success)
073: * ...
074: *
075: */
076:
077: /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
078:
079: byte[] header = new byte[6];
080:
081: if (remote_is.read(header) != 6)
082: throw new IOException("Unexpected EOF on X11 startup!");
083:
084: if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
085: throw new IOException(
086: "Unknown endian format in X11 message!");
087:
088: /* Yes, I came up with this myself - shall I file an application for a patent? =) */
089:
090: int idxMSB = (header[0] == 0x42) ? 0 : 1;
091:
092: /* Read authorization data header */
093:
094: byte[] auth_buff = new byte[6];
095:
096: if (remote_is.read(auth_buff) != 6)
097: throw new IOException("Unexpected EOF on X11 startup!");
098:
099: int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8)
100: | (auth_buff[1 - idxMSB] & 0xff);
101: int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8)
102: | (auth_buff[3 - idxMSB] & 0xff);
103:
104: if ((authProtocolNameLength > 256)
105: || (authProtocolDataLength > 256))
106: throw new IOException("Buggy X11 authorization data");
107:
108: int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
109: int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
110:
111: byte[] authProtocolName = new byte[authProtocolNameLength];
112: byte[] authProtocolData = new byte[authProtocolDataLength];
113:
114: byte[] paddingBuffer = new byte[4];
115:
116: if (remote_is.read(authProtocolName) != authProtocolNameLength)
117: throw new IOException(
118: "Unexpected EOF on X11 startup! (authProtocolName)");
119:
120: if (remote_is.read(paddingBuffer, 0,
121: authProtocolNamePadding) != authProtocolNamePadding)
122: throw new IOException(
123: "Unexpected EOF on X11 startup! (authProtocolNamePadding)");
124:
125: if (remote_is.read(authProtocolData) != authProtocolDataLength)
126: throw new IOException(
127: "Unexpected EOF on X11 startup! (authProtocolData)");
128:
129: if (remote_is.read(paddingBuffer, 0,
130: authProtocolDataPadding) != authProtocolDataPadding)
131: throw new IOException(
132: "Unexpected EOF on X11 startup! (authProtocolDataPadding)");
133:
134: if ("MIT-MAGIC-COOKIE-1"
135: .equals(new String(authProtocolName)) == false)
136: throw new IOException(
137: "Unknown X11 authorization protocol!");
138:
139: if (authProtocolDataLength != 16)
140: throw new IOException(
141: "Wrong data length for X11 authorization data!");
142:
143: StringBuffer tmp = new StringBuffer(32);
144: for (int i = 0; i < authProtocolData.length; i++) {
145: String digit2 = Integer
146: .toHexString(authProtocolData[i] & 0xff);
147: tmp.append((digit2.length() == 2) ? digit2 : "0"
148: + digit2);
149: }
150: String hexEncodedFakeCookie = tmp.toString();
151:
152: /* Order is very important here - it may be that a certain x11 forwarding
153: * gets disabled right in the moment when we check and register our connection
154: * */
155:
156: synchronized (c) {
157: /* Please read the comment in Channel.java */
158: c.hexX11FakeCookie = hexEncodedFakeCookie;
159: }
160:
161: /* Now check our fake cookie directory to see if we produced this cookie */
162:
163: X11ServerData sd = c.cm
164: .checkX11Cookie(hexEncodedFakeCookie);
165:
166: if (sd == null)
167: throw new IOException("Invalid X11 cookie received.");
168:
169: /* If the session which corresponds to this cookie is closed then we will
170: * detect this: the session's close code will close all channels
171: * with the session's assigned x11 fake cookie.
172: */
173:
174: s = new Socket(sd.hostname, sd.port);
175:
176: OutputStream x11_os = s.getOutputStream();
177: InputStream x11_is = s.getInputStream();
178:
179: /* Now we are sending the startup packet to the real X11 server */
180:
181: x11_os.write(header);
182:
183: if (sd.x11_magic_cookie == null) {
184: byte[] emptyAuthData = new byte[6];
185: /* empty auth data, hopefully you are connecting to localhost =) */
186: x11_os.write(emptyAuthData);
187: } else {
188: if (sd.x11_magic_cookie.length != 16)
189: throw new IOException(
190: "The real X11 cookie has an invalid length!");
191:
192: /* send X11 cookie specified by client */
193: x11_os.write(auth_buff);
194: x11_os.write(authProtocolName); /* re-use */
195: x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
196: x11_os.write(sd.x11_magic_cookie);
197: x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
198: }
199:
200: x11_os.flush();
201:
202: /* Start forwarding traffic */
203:
204: StreamForwarder r2l = new StreamForwarder(c, null, null,
205: remote_is, x11_os, "RemoteToX11");
206: StreamForwarder l2r = new StreamForwarder(c, null, null,
207: x11_is, remote_os, "X11ToRemote");
208:
209: /* No need to start two threads, one can be executed in the current thread */
210:
211: r2l.setDaemon(true);
212: r2l.start();
213: l2r.run();
214:
215: while (r2l.isAlive()) {
216: try {
217: r2l.join();
218: } catch (InterruptedException e) {
219: }
220: }
221:
222: /* If the channel is already closed, then this is a no-op */
223:
224: c.cm.closeChannel(c, "EOF on both X11 streams reached.",
225: true);
226: s.close();
227: } catch (IOException e) {
228: log.log(50, "IOException in X11 proxy code: "
229: + e.getMessage());
230:
231: try {
232: c.cm.closeChannel(c, "IOException in X11 proxy code ("
233: + e.getMessage() + ")", true);
234: } catch (IOException e1) {
235: }
236: try {
237: if (s != null)
238: s.close();
239: } catch (IOException e1) {
240: }
241: }
242: }
243: }
|