OSDN Git Service

Initial import from http://code.google.com/p/connectbot/ (r416)
[android-x86/packages-apps-ConnectBot.git] / src / com / trilead / ssh2 / channel / RemoteX11AcceptThread.java
1 \r
2 package com.trilead.ssh2.channel;\r
3 \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
8 \r
9 import com.trilead.ssh2.log.Logger;\r
10 \r
11 \r
12 /**\r
13  * RemoteX11AcceptThread.\r
14  * \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
17  */\r
18 public class RemoteX11AcceptThread extends Thread\r
19 {\r
20         private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);\r
21 \r
22         Channel c;\r
23 \r
24         String remoteOriginatorAddress;\r
25         int remoteOriginatorPort;\r
26 \r
27         Socket s;\r
28 \r
29         public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort)\r
30         {\r
31                 this.c = c;\r
32                 this.remoteOriginatorAddress = remoteOriginatorAddress;\r
33                 this.remoteOriginatorPort = remoteOriginatorPort;\r
34         }\r
35 \r
36         public void run()\r
37         {\r
38                 try\r
39                 {\r
40                         /* Send Open Confirmation */\r
41 \r
42                         c.cm.sendOpenConfirmation(c);\r
43 \r
44                         /* Read startup packet from client */\r
45 \r
46                         OutputStream remote_os = c.getStdinStream();\r
47                         InputStream remote_is = c.getStdoutStream();\r
48 \r
49                         /* The following code is based on the protocol description given in:\r
50                          * Scheifler/Gettys,\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
53                          */\r
54 \r
55                         /*\r
56                          * Client startup:\r
57                          * \r
58                          * 1 0X42 MSB first/0x6c lSB first - byteorder\r
59                          * 1 - unused\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
64                          * 2 - unused\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
69                          * \r
70                          * pad(X) = (4 - (X mod 4)) mod 4\r
71                          * \r
72                          * Server response:\r
73                          * \r
74                          * 1 (0 failed, 2 authenticate, 1 success)\r
75                          * ...\r
76                          * \r
77                          */\r
78 \r
79                         /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */\r
80 \r
81                         byte[] header = new byte[6];\r
82 \r
83                         if (remote_is.read(header) != 6)\r
84                                 throw new IOException("Unexpected EOF on X11 startup!");\r
85 \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
88 \r
89                         /* Yes, I came up with this myself - shall I file an application for a patent? =) */\r
90                         \r
91                         int idxMSB = (header[0] == 0x42) ? 0 : 1;\r
92 \r
93                         /* Read authorization data header */\r
94 \r
95                         byte[] auth_buff = new byte[6];\r
96 \r
97                         if (remote_is.read(auth_buff) != 6)\r
98                                 throw new IOException("Unexpected EOF on X11 startup!");\r
99 \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
102 \r
103                         if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))\r
104                                 throw new IOException("Buggy X11 authorization data");\r
105 \r
106                         int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);\r
107                         int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);\r
108 \r
109                         byte[] authProtocolName = new byte[authProtocolNameLength];\r
110                         byte[] authProtocolData = new byte[authProtocolDataLength];\r
111 \r
112                         byte[] paddingBuffer = new byte[4];\r
113 \r
114                         if (remote_is.read(authProtocolName) != authProtocolNameLength)\r
115                                 throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");\r
116 \r
117                         if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)\r
118                                 throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");\r
119 \r
120                         if (remote_is.read(authProtocolData) != authProtocolDataLength)\r
121                                 throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");\r
122 \r
123                         if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)\r
124                                 throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");\r
125 \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
128 \r
129                         if (authProtocolDataLength != 16)\r
130                                 throw new IOException("Wrong data length for X11 authorization data!");\r
131 \r
132                         StringBuffer tmp = new StringBuffer(32);\r
133                         for (int i = 0; i < authProtocolData.length; i++)\r
134                         {\r
135                                 String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);\r
136                                 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);\r
137                         }\r
138                         String hexEncodedFakeCookie = tmp.toString();\r
139 \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
142                          * */\r
143 \r
144                         synchronized (c)\r
145                         {\r
146                                 /* Please read the comment in Channel.java */\r
147                                 c.hexX11FakeCookie = hexEncodedFakeCookie;\r
148                         }\r
149 \r
150                         /* Now check our fake cookie directory to see if we produced this cookie */\r
151 \r
152                         X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);\r
153 \r
154                         if (sd == null)\r
155                                 throw new IOException("Invalid X11 cookie received.");\r
156 \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
160                          */\r
161 \r
162                         s = new Socket(sd.hostname, sd.port);\r
163 \r
164                         OutputStream x11_os = s.getOutputStream();\r
165                         InputStream x11_is = s.getInputStream();\r
166 \r
167                         /* Now we are sending the startup packet to the real X11 server */\r
168 \r
169                         x11_os.write(header);\r
170 \r
171                         if (sd.x11_magic_cookie == null)\r
172                         {\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
176                         }\r
177                         else\r
178                         {\r
179                                 if (sd.x11_magic_cookie.length != 16)\r
180                                         throw new IOException("The real X11 cookie has an invalid length!");\r
181 \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
188                         }\r
189 \r
190                         x11_os.flush();\r
191 \r
192                         /* Start forwarding traffic */\r
193 \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
196 \r
197                         /* No need to start two threads, one can be executed in the current thread */\r
198 \r
199                         r2l.setDaemon(true);\r
200                         r2l.start();\r
201                         l2r.run();\r
202 \r
203                         while (r2l.isAlive())\r
204                         {\r
205                                 try\r
206                                 {\r
207                                         r2l.join();\r
208                                 }\r
209                                 catch (InterruptedException e)\r
210                                 {\r
211                                 }\r
212                         }\r
213 \r
214                         /* If the channel is already closed, then this is a no-op */\r
215 \r
216                         c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);\r
217                         s.close();\r
218                 }\r
219                 catch (IOException e)\r
220                 {\r
221                         log.log(50, "IOException in X11 proxy code: " + e.getMessage());\r
222 \r
223                         try\r
224                         {\r
225                                 c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true);\r
226                         }\r
227                         catch (IOException e1)\r
228                         {\r
229                         }\r
230                         try\r
231                         {\r
232                                 if (s != null)\r
233                                         s.close();\r
234                         }\r
235                         catch (IOException e1)\r
236                         {\r
237                         }\r
238                 }\r
239         }\r
240 }\r