Mercurial > 510Connectbot
comparison src/ch/ethz/ssh2/channel/RemoteX11AcceptThread.java @ 273:91a31873c42a ganymed
start conversion from trilead to ganymed
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 18 Jul 2014 11:21:46 -0700 |
parents | |
children | 071eccdff8ea |
comparison
equal
deleted
inserted
replaced
272:ce2f4e397703 | 273:91a31873c42a |
---|---|
1 /* | |
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. | |
3 * Please refer to the LICENSE.txt for licensing details. | |
4 */ | |
5 package ch.ethz.ssh2.channel; | |
6 | |
7 import java.io.IOException; | |
8 import java.io.InputStream; | |
9 import java.io.OutputStream; | |
10 import java.net.Socket; | |
11 | |
12 import ch.ethz.ssh2.log.Logger; | |
13 import ch.ethz.ssh2.util.StringEncoder; | |
14 | |
15 /** | |
16 * RemoteX11AcceptThread. | |
17 * | |
18 * @author Christian Plattner | |
19 * @version $Id: RemoteX11AcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $ | |
20 */ | |
21 public class RemoteX11AcceptThread extends Thread | |
22 { | |
23 private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class); | |
24 | |
25 Channel c; | |
26 | |
27 String remoteOriginatorAddress; | |
28 int remoteOriginatorPort; | |
29 | |
30 Socket s; | |
31 | |
32 public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort) | |
33 { | |
34 this.c = c; | |
35 this.remoteOriginatorAddress = remoteOriginatorAddress; | |
36 this.remoteOriginatorPort = remoteOriginatorPort; | |
37 } | |
38 | |
39 @Override | |
40 public void run() | |
41 { | |
42 try | |
43 { | |
44 /* Send Open Confirmation */ | |
45 | |
46 c.cm.sendOpenConfirmation(c); | |
47 | |
48 /* Read startup packet from client */ | |
49 | |
50 OutputStream remote_os = c.getStdinStream(); | |
51 InputStream remote_is = c.getStdoutStream(); | |
52 | |
53 /* The following code is based on the protocol description given in: | |
54 * Scheifler/Gettys, | |
55 * X Windows System: Core and Extension Protocols: | |
56 * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X | |
57 * (from the ETH library - after being here for almost ten | |
58 * years one of the few books I borrowed... sad but true =) | |
59 */ | |
60 | |
61 /* | |
62 * Client startup: | |
63 * | |
64 * 1 0X42 MSB first/0x6c lSB first - byteorder | |
65 * 1 - unused | |
66 * 2 card16 - protocol-major-version | |
67 * 2 card16 - protocol-minor-version | |
68 * 2 n - lenght of authorization-protocol-name | |
69 * 2 d - lenght of authorization-protocol-data | |
70 * 2 - unused | |
71 * string8 - authorization-protocol-name | |
72 * p - unused, p=pad(n) | |
73 * string8 - authorization-protocol-data | |
74 * q - unused, q=pad(d) | |
75 * | |
76 * pad(X) = (4 - (X mod 4)) mod 4 | |
77 * | |
78 * Server response: | |
79 * | |
80 * 1 (0 failed, 2 authenticate, 1 success) | |
81 * ... | |
82 * | |
83 */ | |
84 | |
85 /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */ | |
86 | |
87 byte[] header = new byte[6]; | |
88 | |
89 if (remote_is.read(header) != 6) | |
90 throw new IOException("Unexpected EOF on X11 startup!"); | |
91 | |
92 if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first | |
93 throw new IOException("Unknown endian format in X11 message!"); | |
94 | |
95 /* Yes, I came up with this myself - shall I file an application for a patent? =) */ | |
96 | |
97 int idxMSB = (header[0] == 0x42) ? 0 : 1; | |
98 | |
99 /* Read authorization data header */ | |
100 | |
101 byte[] auth_buff = new byte[6]; | |
102 | |
103 if (remote_is.read(auth_buff) != 6) | |
104 throw new IOException("Unexpected EOF on X11 startup!"); | |
105 | |
106 int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff); | |
107 int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff); | |
108 | |
109 if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256)) | |
110 throw new IOException("Buggy X11 authorization data"); | |
111 | |
112 int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4); | |
113 int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4); | |
114 | |
115 byte[] authProtocolName = new byte[authProtocolNameLength]; | |
116 byte[] authProtocolData = new byte[authProtocolDataLength]; | |
117 | |
118 byte[] paddingBuffer = new byte[4]; | |
119 | |
120 if (remote_is.read(authProtocolName) != authProtocolNameLength) | |
121 throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)"); | |
122 | |
123 if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding) | |
124 throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)"); | |
125 | |
126 if (remote_is.read(authProtocolData) != authProtocolDataLength) | |
127 throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)"); | |
128 | |
129 if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding) | |
130 throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)"); | |
131 | |
132 if ("MIT-MAGIC-COOKIE-1".equals(StringEncoder.GetString(authProtocolName)) == false) | |
133 throw new IOException("Unknown X11 authorization protocol!"); | |
134 | |
135 if (authProtocolDataLength != 16) | |
136 throw new IOException("Wrong data length for X11 authorization data!"); | |
137 | |
138 StringBuilder tmp = new StringBuilder(32); | |
139 for (int i = 0; i < authProtocolData.length; i++) | |
140 { | |
141 String digit2 = Integer.toHexString(authProtocolData[i] & 0xff); | |
142 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); | |
143 } | |
144 String hexEncodedFakeCookie = tmp.toString(); | |
145 | |
146 /* Order is very important here - it may be that a certain x11 forwarding | |
147 * gets disabled right in the moment when we check and register our connection | |
148 * */ | |
149 | |
150 synchronized (c) | |
151 { | |
152 /* Please read the comment in Channel.java */ | |
153 c.hexX11FakeCookie = hexEncodedFakeCookie; | |
154 } | |
155 | |
156 /* Now check our fake cookie directory to see if we produced this cookie */ | |
157 | |
158 X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie); | |
159 | |
160 if (sd == null) | |
161 throw new IOException("Invalid X11 cookie received."); | |
162 | |
163 /* If the session which corresponds to this cookie is closed then we will | |
164 * detect this: the session's close code will close all channels | |
165 * with the session's assigned x11 fake cookie. | |
166 */ | |
167 | |
168 s = new Socket(sd.hostname, sd.port); | |
169 | |
170 OutputStream x11_os = s.getOutputStream(); | |
171 InputStream x11_is = s.getInputStream(); | |
172 | |
173 /* Now we are sending the startup packet to the real X11 server */ | |
174 | |
175 x11_os.write(header); | |
176 | |
177 if (sd.x11_magic_cookie == null) | |
178 { | |
179 byte[] emptyAuthData = new byte[6]; | |
180 /* empty auth data, hopefully you are connecting to localhost =) */ | |
181 x11_os.write(emptyAuthData); | |
182 } | |
183 else | |
184 { | |
185 if (sd.x11_magic_cookie.length != 16) | |
186 throw new IOException("The real X11 cookie has an invalid length!"); | |
187 | |
188 /* send X11 cookie specified by client */ | |
189 x11_os.write(auth_buff); | |
190 x11_os.write(authProtocolName); /* re-use */ | |
191 x11_os.write(paddingBuffer, 0, authProtocolNamePadding); | |
192 x11_os.write(sd.x11_magic_cookie); | |
193 x11_os.write(paddingBuffer, 0, authProtocolDataPadding); | |
194 } | |
195 | |
196 x11_os.flush(); | |
197 | |
198 /* Start forwarding traffic */ | |
199 | |
200 StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11"); | |
201 StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote"); | |
202 | |
203 /* No need to start two threads, one can be executed in the current thread */ | |
204 | |
205 r2l.setDaemon(true); | |
206 r2l.start(); | |
207 l2r.run(); | |
208 | |
209 while (r2l.isAlive()) | |
210 { | |
211 try | |
212 { | |
213 r2l.join(); | |
214 } | |
215 catch (InterruptedException ignored) | |
216 { | |
217 } | |
218 } | |
219 | |
220 /* If the channel is already closed, then this is a no-op */ | |
221 | |
222 c.cm.closeChannel(c, "EOF on both X11 streams reached.", true); | |
223 s.close(); | |
224 } | |
225 catch (IOException e) | |
226 { | |
227 log.warning("IOException in X11 proxy code: " + e.getMessage()); | |
228 | |
229 try | |
230 { | |
231 c.cm.closeChannel(c, e, true); | |
232 } | |
233 catch (IOException ignored) | |
234 { | |
235 } | |
236 try | |
237 { | |
238 if (s != null) | |
239 s.close(); | |
240 } | |
241 catch (IOException ignored) | |
242 { | |
243 } | |
244 } | |
245 } | |
246 } |