source: other-projects/trunk/gs3-release-maker/tasks/sshtaskdef/src/mindbright/ssh/SSHChannelController.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 12.2 KB
Line 
1/******************************************************************************
2 *
3 * Copyright (c) 1998,99 by Mindbright Technology AB, Stockholm, Sweden.
4 * www.mindbright.se, [email protected]
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 *****************************************************************************
17 * $Author: mats $
18 * $Date: 2000/03/09 10:59:44 $
19 * $Name: rel1-2-1 $
20 *****************************************************************************/
21package mindbright.ssh;
22
23import java.io.*;
24import java.util.Vector;
25
26import mindbright.security.*;
27
28public final class SSHChannelController extends SSH implements SSHChannelListener {
29
30 protected SSHTxChannel txChan;
31 protected SSHRxChannel rxChan;
32 protected SSHConnectChannel cnChan;
33 protected SSHPduQueue txQueue;
34 protected SSHPduQueue cnQueue;
35
36 protected int totalTunnels;
37 protected int nextEmptyChan;
38 protected Object[] tunnels;
39
40 protected Vector listenChannels;
41
42 protected SSH sshHook;
43 protected SSHConsole console;
44
45 protected Cipher sndCipher;
46 protected Cipher rcvCipher;
47
48 public SSHChannelController(SSH sshHook, InputStream in, OutputStream out,
49 Cipher sndCipher, Cipher rcvCipher,
50 SSHConsole console, boolean haveCnxWatch) {
51 this.sndCipher = sndCipher;
52 this.rcvCipher = rcvCipher;
53
54 this.sshHook = sshHook;
55 this.console = console;
56
57 this.tunnels = new Object[16];
58 this.nextEmptyChan = 0;
59 this.totalTunnels = 0;
60 this.listenChannels = new Vector();
61
62 txChan = new SSHTxChannel(out, MAIN_CHAN_NUM);
63 rxChan = new SSHRxChannel(in, MAIN_CHAN_NUM);
64
65 rxChan.setSSHChannelListener(this);
66 txChan.setSSHChannelListener(this);
67 rxChan.setSSHPduFactory(new SSHPduInputStream(MSG_ANY, rcvCipher));
68 txQueue = txChan.getQueue();
69
70 if(haveCnxWatch) {
71 cnChan = new SSHConnectChannel(this);
72 cnChan.setSSHChannelListener(this);
73 cnQueue = cnChan.getQueue();
74 } else {
75 cnQueue = new SSHPduQueue();
76 }
77
78 }
79
80 public void start() {
81 txChan.start();
82 rxChan.start();
83 if(cnChan != null)
84 cnChan.start();
85 }
86
87 public void waitForExit() throws InterruptedException {
88 waitForExit(0); // Wait forever...
89 }
90 public void waitForExit(long msWait) throws InterruptedException {
91 if(rxChan != null)
92 rxChan.join(msWait);
93 Thread.sleep(100);
94 killAll();
95 }
96
97 public void killAll() {
98 killAllTunnels();
99 killListenChannels();
100 if(rxChan != null && rxChan.isAlive())
101 rxChan.stop();
102 if(txChan != null && txChan.isAlive())
103 txChan.stop();
104 if(cnChan != null && cnChan.isAlive())
105 cnChan.stop();
106
107 rxChan = null;
108 txChan = null;
109 cnChan = null;
110 System.runFinalization();
111 }
112
113 public synchronized int newChannelId() {
114 int newChan = nextEmptyChan;
115 if(nextEmptyChan < tunnels.length) {
116 int i;
117 for(i = nextEmptyChan + 1; i < tunnels.length; i++)
118 if(tunnels[i] == null)
119 break;
120 nextEmptyChan = i;
121 } else {
122 Object[] tmp = new Object[tunnels.length + 16];
123 System.arraycopy(tunnels, 0, tmp, 0, tunnels.length);
124 tunnels = tmp;
125 nextEmptyChan++;
126 }
127
128 return newChan;
129 }
130
131 public synchronized String[] listTunnels() {
132 int i, cnt = 0;
133 String[] list1 = new String[tunnels.length];
134
135 for(i = 0; i < tunnels.length; i++) {
136 if(tunnels[i] == null)
137 continue;
138 list1[cnt++] = ((SSHTunnel)tunnels[i]).getDescription();
139 }
140
141 String[] list2 = new String[cnt];
142 System.arraycopy(list1, 0, list2, 0, cnt);
143
144 return list2;
145 }
146
147 public synchronized void closeTunnelFromList(int listIdx) {
148 int i;
149 for(i = 0; i < tunnels.length; i++) {
150 if(tunnels[i] == null)
151 continue;
152 listIdx--;
153 if(listIdx < 0)
154 break;
155 }
156 if(i < tunnels.length) {
157 ((SSHTunnel)tunnels[i]).terminateNow();
158 }
159 }
160
161 public synchronized void killAllTunnels() {
162 for(int i = 0; i < tunnels.length; i++) {
163 if(tunnels[i] == null)
164 continue;
165 ((SSHTunnel)tunnels[i]).openFailure(); // !!! Forced close
166 tunnels[i] = null;
167 }
168 tunnels = new Object[16];
169 }
170
171 public synchronized void addTunnel(SSHTunnel tunnel) throws IOException {
172 totalTunnels++;
173 tunnels[tunnel.channelId] = tunnel;
174 }
175
176 public synchronized SSHTunnel delTunnel(int channelId) {
177 SSHTunnel tunnelToDelete = (SSHTunnel) tunnels[channelId];
178 tunnels[channelId] = null;
179 nextEmptyChan = (channelId < nextEmptyChan ? channelId : nextEmptyChan);
180 totalTunnels--;
181 return tunnelToDelete;
182 }
183
184 public boolean haveHostInFwdOpen() {
185 return sshHook.isProtocolFlagSet(PROTOFLAG_HOST_IN_FWD_OPEN);
186 }
187
188 public SSHListenChannel newListenChannel(String localHost, int localPort,
189 String remoteHost, int remotePort,
190 String plugin) throws IOException {
191 SSHListenChannel newListenChan = null;
192 newListenChan = SSHProtocolPlugin.getPlugin(plugin).localListener(localHost, localPort,
193 remoteHost, remotePort,
194 this);
195 newListenChan.setSSHChannelListener(this);
196 newListenChan.start();
197 synchronized(listenChannels) {
198 listenChannels.addElement(newListenChan);
199 }
200 return newListenChan;
201 }
202
203 public void killListenChannel(String localHost, int listenPort) {
204 SSHListenChannel listenChan;
205 synchronized(listenChannels) {
206 for(int i = 0; i < listenChannels.size(); i++) {
207 listenChan = (SSHListenChannel) listenChannels.elementAt(i);
208 if(listenChan.getListenPort() == listenPort && listenChan.getListenHost().equals(localHost)) {
209 listenChannels.removeElementAt(i);
210 listenChan.forceClose();
211 break;
212 }
213 }
214 }
215 }
216
217 public void killListenChannels() {
218 SSHListenChannel listenChan;
219 synchronized(listenChannels) {
220 while(listenChannels.size() > 0) {
221 listenChan = (SSHListenChannel) listenChannels.elementAt(0);
222 listenChan.forceClose();
223 listenChannels.removeElementAt(0);
224 }
225 }
226 }
227
228 public SSHPdu prepare(SSHPdu pdu) {
229 return pdu;
230 }
231
232 public void transmit(SSHPdu pdu) {
233 txQueue.putLast(pdu);
234 }
235
236 public void receive(SSHPdu pdu) {
237 SSHPduInputStream inPdu = (SSHPduInputStream) pdu;
238 SSHTunnel tunnel;
239 int channelNum;
240 try {
241 switch(inPdu.type) {
242 case SMSG_STDOUT_DATA:
243 if(console != null)
244 console.stdoutWriteString(inPdu.readStringAsBytes());
245 break;
246 case SMSG_STDERR_DATA:
247 if(console != null)
248 console.stderrWriteString(inPdu.readStringAsBytes());
249 break;
250 case SMSG_EXITSTATUS:
251 SSHPduOutputStream exitPdu = new SSHPduOutputStream(CMSG_EXIT_CONFIRMATION, sndCipher);
252 int status = inPdu.readInt();
253 if(console != null) {
254 if(status != 0)
255 console.serverDisconnect(sshAsClient().getServerAddr().getHostName() + " disconnected: " + status);
256 else
257 console.serverDisconnect("Connection to " + sshAsClient().getServerAddr().getHostName() + " closed.");
258 }
259 transmit(exitPdu);
260 sshAsClient().disconnect(true);
261 break;
262 case SMSG_X11_OPEN:
263 // Fallthrough
264 case MSG_PORT_OPEN:
265 cnQueue.putLast(inPdu);
266 break;
267 case MSG_CHANNEL_DATA:
268 channelNum = inPdu.readInt();
269 tunnel = (SSHTunnel)tunnels[channelNum];
270 if(tunnel != null)
271 tunnel.transmit(pdu);
272 else
273 throw new Exception("Data on nonexistent channel: " + channelNum);
274 break;
275 case MSG_CHANNEL_OPEN_CONFIRMATION:
276 channelNum = inPdu.readInt();
277 tunnel = (SSHTunnel)tunnels[channelNum];
278 if(tunnel != null) {
279 if(!tunnel.setRemoteChannelId(inPdu.readInt()))
280 throw new Exception("Open confirmation on allready opened channel!");
281 tunnel.start();
282 } else
283 throw new Exception("Open confirm on nonexistent: " + channelNum);
284 break;
285 case MSG_CHANNEL_OPEN_FAILURE:
286 SSHTunnel failTunnel;
287 channelNum = inPdu.readInt();
288 if((failTunnel = delTunnel(channelNum)) != null) {
289 alert("Channel open failure on " + failTunnel.remoteDesc);
290 failTunnel.openFailure();
291 } else
292 throw new Exception("Open failure on nonexistent channel: " + channelNum);
293 break;
294 case MSG_CHANNEL_INPUT_EOF:
295 channelNum = inPdu.readInt();
296 tunnel = (SSHTunnel)tunnels[channelNum];
297 if(tunnel != null) {
298 tunnel.receiveInputEOF();
299 } else
300 throw new Exception("Input eof on nonexistent channel: " + channelNum);
301 break;
302 case MSG_CHANNEL_OUTPUT_CLOSED:
303 channelNum = inPdu.readInt();
304 ;
305 if(channelNum < tunnels.length && ((tunnel = (SSHTunnel)tunnels[channelNum]) != null)) {
306 tunnel.receiveOutputClosed();
307 } else
308 throw new Exception("Output closed on nonexistent channel: " + channelNum);
309 break;
310 case MSG_DISCONNECT:
311 disconnect("Peer disconnected: " + inPdu.readString());
312 break;
313 case CMSG_WINDOW_SIZE:
314 break;
315 case CMSG_STDIN_DATA:
316 break;
317 case CMSG_EOF:
318 System.out.println("!!! EOF received...");
319 break;
320 case CMSG_EXIT_CONFIRMATION:
321 break;
322 default:
323 throw new Exception("Unknown packet type (" + inPdu.type + "), disconnecting...");
324 }
325 } catch(Exception e) {
326 // !!! Are there known BUGS in here?? Nah... :-)
327 StringWriter sw = new StringWriter();
328 e.printStackTrace(new PrintWriter(sw));
329 System.out.println("\nplease send a mail to [email protected] with:");
330 System.out.println("(I found a bug in MindTerm!), error: " + e.getMessage());
331 System.out.println(sw.toString());
332 sendDisconnect("please send a mail to [email protected] with:" + "\n\r" +
333 "(I found a bug in MindTerm!), error: " + e.getMessage() + "\n\r" +
334 kludgeLF2CRLFMap(sw.toString()));
335 // !!!
336 }
337 }
338
339 static String kludgeLF2CRLFMap(String orig) {
340 int o = 0, n;
341 String result = "";
342 while((n = orig.indexOf('\n', o)) != -1) {
343 result += orig.substring(o, n) + "\n\r";
344 o = n + 1;
345 }
346 result += orig.substring(o);
347 return result;
348 }
349
350 public void close(SSHChannel chan) {
351 // !!!
352 if(chan instanceof SSHConnectChannel)
353 SSH.logExtra("Controller connect-channel closed");
354 else if(chan instanceof SSHTxChannel)
355 SSH.logExtra("Controller TX-channel closed");
356 else if(chan instanceof SSHRxChannel)
357 SSH.logExtra("Controller RX-channel closed");
358 else if(chan instanceof SSHListenChannel)
359 SSH.logExtra("Listen channel for port " + ((SSHListenChannel)chan).getListenPort() +
360 " closed");
361 else
362 alert("Bug in SSHChannelController.close 'chan' is: " + chan);
363 }
364
365 public void disconnect(String reason) {
366 if(sshHook.isAnSSHClient)
367 sshAsClient().disconnect(false);
368 if(txChan != null)
369 txChan.setClosePending();
370 if(console != null)
371 console.serverDisconnect("\r\nDisconnecting, " + reason);
372 else
373 SSH.log("\r\nDisconnecting, " + reason);
374
375 if(!sshHook.isAnSSHClient && rxChan != null) {
376 rxChan.forceClose();
377 }
378 }
379
380 public void sendDisconnect(String reason) {
381 try {
382 SSHPduOutputStream pdu = new SSHPduOutputStream(MSG_DISCONNECT, sndCipher);
383 pdu.writeString(reason);
384 if(txQueue != null)
385 txQueue.putFirst(pdu);
386 Thread.sleep(300);
387 disconnect(reason);
388 } catch (Exception e) {
389 alert("Error in sendDisconnect: " + e.toString());
390 }
391 }
392
393 public void alert(String msg) {
394 if(sshHook.isAnSSHClient) {
395 SSHInteractor interactor = sshAsClient().user.getInteractor();
396 if(interactor != null)
397 interactor.alert(msg);
398 } else {
399 SSH.log(msg);
400 }
401 }
402
403 protected SSHClient sshAsClient() {
404 return (SSHClient)sshHook;
405 }
406
407 public SSHPduQueue getCnQueue() {
408 return cnQueue;
409 }
410
411 public void addHostMapTemporary(String fromHost, String toHost, int toPort) {
412 cnChan.addHostMapTemporary(fromHost, toHost, toPort);
413 }
414
415 public void addHostMapPermanent(String fromHost, String toHost, int toPort) {
416 cnChan.addHostMapPermanent(fromHost, toHost, toPort);
417 }
418
419 public void delHostMap(String fromHost) {
420 cnChan.delHostMap(fromHost);
421 }
422
423 public Vector getHostMap(String fromHost) {
424 return cnChan.getHostMap(fromHost);
425 }
426
427}
Note: See TracBrowser for help on using the repository browser.