Mercurial > 510Connectbot
comparison src/com/trilead/ssh2/channel/DynamicAcceptThread.java @ 0:0ce5cc452d02
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 22 May 2014 10:41:19 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:0ce5cc452d02 |
---|---|
1 /* | |
2 * ConnectBot: simple, powerful, open-source SSH client for Android | |
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey | |
4 * | |
5 * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 * you may not use this file except in compliance with the License. | |
7 * You may obtain a copy of the License at | |
8 * | |
9 * http://www.apache.org/licenses/LICENSE-2.0 | |
10 * | |
11 * Unless required by applicable law or agreed to in writing, software | |
12 * distributed under the License is distributed on an "AS IS" BASIS, | |
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 * See the License for the specific language governing permissions and | |
15 * limitations under the License. | |
16 */ | |
17 | |
18 package com.trilead.ssh2.channel; | |
19 | |
20 import java.io.IOException; | |
21 import java.io.InputStream; | |
22 import java.io.InterruptedIOException; | |
23 import java.io.OutputStream; | |
24 import java.io.PushbackInputStream; | |
25 import java.net.ConnectException; | |
26 import java.net.InetAddress; | |
27 import java.net.InetSocketAddress; | |
28 import java.net.NoRouteToHostException; | |
29 import java.net.ServerSocket; | |
30 import java.net.Socket; | |
31 | |
32 import net.sourceforge.jsocks.Proxy; | |
33 import net.sourceforge.jsocks.ProxyMessage; | |
34 import net.sourceforge.jsocks.Socks4Message; | |
35 import net.sourceforge.jsocks.Socks5Message; | |
36 import net.sourceforge.jsocks.SocksException; | |
37 import net.sourceforge.jsocks.server.ServerAuthenticator; | |
38 import net.sourceforge.jsocks.server.ServerAuthenticatorNone; | |
39 | |
40 /** | |
41 * DynamicAcceptThread. | |
42 * | |
43 * @author Kenny Root | |
44 * @version $Id$ | |
45 */ | |
46 public class DynamicAcceptThread extends Thread implements IChannelWorkerThread { | |
47 private ChannelManager cm; | |
48 private ServerSocket ss; | |
49 | |
50 class DynamicAcceptRunnable implements Runnable { | |
51 private static final int idleTimeout = 180000; //3 minutes | |
52 | |
53 private ServerAuthenticator auth; | |
54 private Socket sock; | |
55 private InputStream in; | |
56 private OutputStream out; | |
57 private ProxyMessage msg; | |
58 | |
59 public DynamicAcceptRunnable(ServerAuthenticator auth, Socket sock) { | |
60 this.auth = auth; | |
61 this.sock = sock; | |
62 setName("DynamicAcceptRunnable"); | |
63 } | |
64 | |
65 public void run() { | |
66 try { | |
67 startSession(); | |
68 } | |
69 catch (IOException ioe) { | |
70 int error_code = Proxy.SOCKS_FAILURE; | |
71 | |
72 if (ioe instanceof SocksException) | |
73 error_code = ((SocksException) ioe).errCode; | |
74 else if (ioe instanceof NoRouteToHostException) | |
75 error_code = Proxy.SOCKS_HOST_UNREACHABLE; | |
76 else if (ioe instanceof ConnectException) | |
77 error_code = Proxy.SOCKS_CONNECTION_REFUSED; | |
78 else if (ioe instanceof InterruptedIOException) | |
79 error_code = Proxy.SOCKS_TTL_EXPIRE; | |
80 | |
81 if (error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED | |
82 || error_code < 0) { | |
83 error_code = Proxy.SOCKS_FAILURE; | |
84 } | |
85 | |
86 sendErrorMessage(error_code); | |
87 } | |
88 finally { | |
89 if (auth != null) | |
90 auth.endSession(); | |
91 } | |
92 } | |
93 | |
94 private ProxyMessage readMsg(InputStream in) throws IOException { | |
95 PushbackInputStream push_in; | |
96 | |
97 if (in instanceof PushbackInputStream) | |
98 push_in = (PushbackInputStream) in; | |
99 else | |
100 push_in = new PushbackInputStream(in); | |
101 | |
102 int version = push_in.read(); | |
103 push_in.unread(version); | |
104 ProxyMessage msg; | |
105 | |
106 if (version == 5) { | |
107 msg = new Socks5Message(push_in, false); | |
108 } | |
109 else if (version == 4) { | |
110 msg = new Socks4Message(push_in, false); | |
111 } | |
112 else { | |
113 throw new SocksException(Proxy.SOCKS_FAILURE); | |
114 } | |
115 | |
116 return msg; | |
117 } | |
118 | |
119 private void sendErrorMessage(int error_code) { | |
120 ProxyMessage err_msg; | |
121 | |
122 if (msg instanceof Socks4Message) | |
123 err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); | |
124 else | |
125 err_msg = new Socks5Message(error_code); | |
126 | |
127 try { | |
128 err_msg.write(out); | |
129 } | |
130 catch (IOException ioe) { | |
131 } | |
132 } | |
133 | |
134 private void handleRequest(ProxyMessage msg) throws IOException { | |
135 if (!auth.checkRequest(msg)) | |
136 throw new SocksException(Proxy.SOCKS_FAILURE); | |
137 | |
138 switch (msg.command) { | |
139 case Proxy.SOCKS_CMD_CONNECT: | |
140 onConnect(msg); | |
141 break; | |
142 | |
143 default: | |
144 throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED); | |
145 } | |
146 } | |
147 | |
148 private void startSession() throws IOException { | |
149 sock.setSoTimeout(idleTimeout); | |
150 | |
151 try { | |
152 auth = auth.startSession(sock); | |
153 } | |
154 catch (IOException ioe) { | |
155 System.out.println("Could not start SOCKS session"); | |
156 ioe.printStackTrace(); | |
157 auth = null; | |
158 return; | |
159 } | |
160 | |
161 if (auth == null) { // Authentication failed | |
162 System.out.println("SOCKS auth failed"); | |
163 return; | |
164 } | |
165 | |
166 in = auth.getInputStream(); | |
167 out = auth.getOutputStream(); | |
168 msg = readMsg(in); | |
169 handleRequest(msg); | |
170 } | |
171 | |
172 private void onConnect(ProxyMessage msg) throws IOException { | |
173 ProxyMessage response = null; | |
174 Channel cn = null; | |
175 StreamForwarder r2l = null; | |
176 StreamForwarder l2r = null; | |
177 | |
178 if (msg instanceof Socks5Message) { | |
179 response = new Socks5Message(Proxy.SOCKS_SUCCESS, (InetAddress)null, 0); | |
180 } | |
181 else { | |
182 response = new Socks4Message(Socks4Message.REPLY_OK, (InetAddress)null, 0); | |
183 } | |
184 | |
185 response.write(out); | |
186 String destHost = msg.host; | |
187 | |
188 if (msg.ip != null) | |
189 destHost = msg.ip.getHostAddress(); | |
190 | |
191 try { | |
192 /* | |
193 * This may fail, e.g., if the remote port is closed (in | |
194 * optimistic terms: not open yet) | |
195 */ | |
196 cn = cm.openDirectTCPIPChannel(destHost, msg.port, | |
197 "127.0.0.1", 0); | |
198 } | |
199 catch (IOException e) { | |
200 /* | |
201 * Simply close the local socket and wait for the next incoming | |
202 * connection | |
203 */ | |
204 try { | |
205 sock.close(); | |
206 } | |
207 catch (IOException ignore) { | |
208 } | |
209 | |
210 return; | |
211 } | |
212 | |
213 try { | |
214 r2l = new StreamForwarder(cn, null, sock, cn.stdoutStream, out, "RemoteToLocal"); | |
215 l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote"); | |
216 } | |
217 catch (IOException e) { | |
218 try { | |
219 /* | |
220 * This message is only visible during debugging, since we | |
221 * discard the channel immediatelly | |
222 */ | |
223 cn.cm.closeChannel(cn, | |
224 "Weird error during creation of StreamForwarder (" | |
225 + e.getMessage() + ")", true); | |
226 } | |
227 catch (IOException ignore) { | |
228 } | |
229 | |
230 return; | |
231 } | |
232 | |
233 r2l.setDaemon(true); | |
234 l2r.setDaemon(true); | |
235 r2l.start(); | |
236 l2r.start(); | |
237 } | |
238 } | |
239 | |
240 public DynamicAcceptThread(ChannelManager cm, int local_port) | |
241 throws IOException { | |
242 this.cm = cm; | |
243 setName("DynamicAcceptThread"); | |
244 ss = new ServerSocket(local_port); | |
245 } | |
246 | |
247 public DynamicAcceptThread(ChannelManager cm, InetSocketAddress localAddress) | |
248 throws IOException { | |
249 this.cm = cm; | |
250 ss = new ServerSocket(); | |
251 ss.bind(localAddress); | |
252 } | |
253 | |
254 @Override | |
255 public void run() { | |
256 try { | |
257 cm.registerThread(this); | |
258 } | |
259 catch (IOException e) { | |
260 stopWorking(); | |
261 return; | |
262 } | |
263 | |
264 while (true) { | |
265 Socket sock = null; | |
266 | |
267 try { | |
268 sock = ss.accept(); | |
269 } | |
270 catch (IOException e) { | |
271 stopWorking(); | |
272 return; | |
273 } | |
274 | |
275 DynamicAcceptRunnable dar = new DynamicAcceptRunnable(new ServerAuthenticatorNone(), sock); | |
276 Thread t = new Thread(dar); | |
277 t.setDaemon(true); | |
278 t.start(); | |
279 } | |
280 } | |
281 | |
282 /* | |
283 * (non-Javadoc) | |
284 * | |
285 * @see com.trilead.ssh2.channel.IChannelWorkerThread#stopWorking() | |
286 */ | |
287 public void stopWorking() { | |
288 try { | |
289 /* This will lead to an IOException in the ss.accept() call */ | |
290 ss.close(); | |
291 } | |
292 catch (IOException e) { | |
293 } | |
294 } | |
295 } |