Skip to main content
summaryrefslogtreecommitdiffstats
blob: 2beef7f5a31170e8e07aace2c8709079d0692467 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*******************************************************************************
 * Copyright (c) 2007, 2018 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jsch.internal.core;

import java.io.*;
import java.lang.reflect.*;
import java.net.*;

import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;

import com.jcraft.jsch.SocketFactory;

public class ResponsiveSocketFactory implements SocketFactory {
  private static final String JAVA_NET_PROXY="java.net.Proxy"; //$NON-NLS-1$
  private static final int DEFAULT_TIMEOUT=60; // Seconds
  InputStream in = null;
  OutputStream out = null;
  private IProgressMonitor monitor;
  private final int timeout;
  private static Class<?> proxyClass;
  private static boolean hasProxyClass = true;
  public ResponsiveSocketFactory(IProgressMonitor monitor, int timeout) {
    if (monitor == null)
      monitor = new NullProgressMonitor();
    this.monitor = monitor;
    this.timeout=timeout;
  }
  public InputStream getInputStream(Socket socket) throws IOException {
    if (in == null)
      in = socket.getInputStream();
    return in;
  }
  public OutputStream getOutputStream(Socket socket) throws IOException {
    if (out == null)
      out = socket.getOutputStream();
    return out;
  }
  public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    Socket socket = null;
    socket = createSocket(host, port, timeout / 1000, monitor);
    // Null out the monitor so we don't hold onto anything
    // (i.e. the SSH2 session will keep a handle to the socket factory around
    monitor = new NullProgressMonitor();
    // Set the socket timeout
    socket.setSoTimeout(timeout);
    return socket;
  }
  
  /**
   * Helper method that will time out when making a socket connection.
   * This is required because there is no way to provide a timeout value
   * when creating a socket and in some instances, they don't seem to
   * timeout at all.
   */
  private Socket createSocket(final String host, final int port, int timeout, IProgressMonitor monitor) throws UnknownHostException, IOException {
    
    // Start a thread to open a socket
    final Socket[] socket = new Socket[] { null };
    final Exception[] exception = new Exception[] {null };
    final Thread thread = new Thread(new Runnable() {
      public void run() {
        try {
          Socket newSocket = internalCreateSocket(host, port);
          synchronized (socket) {
            if (Thread.interrupted()) {
              // we we're either canceled or timed out so just close the socket
              newSocket.close();
            } else {
              socket[0] = newSocket;
            }
          }
        } catch (UnknownHostException e) {
          exception[0] = e;
        } catch (IOException e) {
          exception[0] = e;
        }
      }
    });
    thread.start();
    
    // Wait the appropriate number of seconds
    if (timeout == 0) timeout = DEFAULT_TIMEOUT;
    for (int i = 0; i < timeout; i++) {
      try {
        // wait for the thread to complete or 1 second, which ever comes first
        thread.join(1000);
      } catch (InterruptedException e) {
        // I think this means the thread was interrupted but not necessarily timed out
        // so we don't need to do anything
      }
      synchronized (socket) {
        // if the user canceled, clean up before preempting the operation
        if (monitor.isCanceled()) {
          if (thread.isAlive()) {
            thread.interrupt();
          }
          if (socket[0] != null) {
            socket[0].close();
          }
          // this method will throw the proper exception
          Policy.checkCanceled(monitor);
        }
      }
    }
    // If the thread is still running (i.e. we timed out) signal that it is too late
    synchronized (socket) {
      if (thread.isAlive()) {
        thread.interrupt();
      }
    }
    if (exception[0] != null) {
      if (exception[0] instanceof UnknownHostException)
        throw (UnknownHostException)exception[0];
      else
        throw (IOException)exception[0];
    }
    if (socket[0] == null) {
      throw new InterruptedIOException(NLS.bind(Messages.Util_timeout, new String[] { host })); 
    }
    return socket[0];
  }
  
  /* private */  Socket internalCreateSocket(final String host, final int port)
      throws UnknownHostException, IOException{
    Class<?> proxyClass = getProxyClass();
    if (proxyClass != null) {
      // We need to disable proxy support for the socket
      try{
        
        // Obtain the value of the NO_PROXY static field of the proxy class
        Field field = proxyClass.getField("NO_PROXY"); //$NON-NLS-1$
        Object noProxyObject = field.get(null);
        Constructor<Socket> constructor = Socket.class.getConstructor(proxyClass);
        Object o = constructor.newInstance(noProxyObject);
        if(o instanceof Socket){
          Socket socket=(Socket)o;
          socket.connect(new InetSocketAddress(host, port), timeout * 1000);
          return socket;
        }
      }
      catch(SecurityException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      catch(NoSuchFieldException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      catch(IllegalArgumentException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      catch(IllegalAccessException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      catch(NoSuchMethodException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      catch(InstantiationException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      catch(InvocationTargetException e){
        JSchCorePlugin.log(IStatus.ERROR, NLS.bind("An internal error occurred while connecting to {0}", host), e); //$NON-NLS-1$
      }
      
    }
    return new Socket(host, port);
  }
  
  private synchronized Class<?> getProxyClass() {
    if (hasProxyClass && proxyClass == null) {
      try{
        proxyClass = Class.forName(JAVA_NET_PROXY);
      }
      catch(ClassNotFoundException e){
        // We couldn't find the class so we'll assume we are using pre-1.5 JRE
        hasProxyClass = false;
      }
    }
    return proxyClass;
  }

}

Back to the top