public boolean enablePortForward() { /* * DynamicPortForwarder dpf = null; * * try { dpf = connection.createDynamicPortForwarder(new * InetSocketAddress( InetAddress.getLocalHost(), 1984)); } catch * (Exception e) { Log.e(TAG, "Could not create dynamic port forward", * e); return false; } */ // LocalPortForwarder lpf1 = null; try { dnspf = connection.createLocalPortForwarder(8053, "www.google.com", 80); if (profile.isSocks()) { dpf = connection.createDynamicPortForwarder(profile.getLocalPort()); } else { lpf = connection.createLocalPortForwarder( profile.getLocalPort(), profile.getRemoteAddress(), profile.getRemotePort()); } } catch (Exception e) { Log.e(TAG, "Could not create local port forward", e); if (reason == null) reason = getString(R.string.fail_to_forward); return false; } return true; }
private void flushIptables() { StringBuffer cmd = new StringBuffer(); cmd.append(BASE + "iptables -t nat -F SSHTUNNEL\n"); cmd.append(BASE + "iptables -t nat -X SSHTUNNEL\n"); cmd.append((CMD_IPTABLES_RETURN.replace("0.0.0.0", hostAddress)).replace("-A", "-D")); if (enableDNSProxy) { cmd.append(BASE + "iptables -t nat -F SSHTUNNELDNS\n"); cmd.append(BASE + "iptables -t nat -X SSHTUNNELDNS\n"); cmd.append(BASE + "iptables -t nat -D OUTPUT -p udp -j SSHTUNNELDNS\n"); } if (profile.isGFWList()) { String[] gfw_list = getResources().getStringArray(R.array.gfw_list); for (String item : gfw_list) { cmd.append(BASE + "iptables -t nat -D OUTPUT -p tcp -d " + item + " -j SSHTUNNEL\n"); } } else if (profile.isAutoSetProxy()) { cmd.append(BASE + "iptables -t nat -D OUTPUT -p tcp -j SSHTUNNEL\n"); } else { // for proxy specified apps if (apps == null || apps.length <= 0) apps = AppManager.getProxyedApps(this, profile.getProxyedApps()); for (int i = 0; i < apps.length; i++) { if (apps[i].isProxyed()) { cmd.append( BASE + "iptables " + "-t nat -m owner --uid-owner " + apps[i].getUid() + " -D OUTPUT -p tcp -j SSHTUNNEL\n"); } } } String rules = cmd.toString(); runRootCommand(rules); if (profile.isSocks()) runRootCommand(BASE + "proxy_socks.sh stop"); else runRootCommand(BASE + "proxy_http.sh stop"); }
// XXX: Is it right? @Override public String[] replyToChallenge( String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) throws Exception { String[] responses = new String[numPrompts]; for (int i = 0; i < numPrompts; i++) { // request response from user for each prompt if (prompt[i].toLowerCase().contains("password")) responses[i] = profile.getPassword(); } return responses; }
@Override public boolean verifyServerHostKey( String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception { String fingerPrint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); int fingerPrintStatus = Constraints.FINGER_PRINT_CHANGED; if (profile.getFingerPrintType() == null || profile.getFingerPrintType().equals("")) { fingerPrintStatus = Constraints.FINGER_PRINT_INIITIALIZE; reason = getString(R.string.finger_print_unknown); } else { if (profile.getFingerPrintType().equals(serverHostKeyAlgorithm) && profile.getFingerPrint() != null && profile.getFingerPrint().equals(fingerPrint)) { return true; } else { fingerPrintStatus = Constraints.FINGER_PRINT_CHANGED; reason = getString(R.string.finger_print_mismatch); } } Log.d(TAG, "Finger Print: " + profile.getFingerPrint()); Log.d(TAG, "Finger Print Type: " + profile.getFingerPrintType()); Bundle bundle = new Bundle(); bundle.putInt(Constraints.ID, profile.getId()); bundle.putInt(Constraints.FINGER_PRINT_STATUS, fingerPrintStatus); bundle.putString(Constraints.FINGER_PRINT, fingerPrint); bundle.putString(Constraints.FINGER_PRINT_TYPE, serverHostKeyAlgorithm); Message msg = new Message(); msg.setData(bundle); hostKeyHandler.sendMessage(msg); synchronized (fingerPrintLock) { fingerPrintLock.wait(); } return fingerPrintChecker; }
// This is the old onStart method that will be called on the pre-2.0 // platform. On 2.0 or later we override onStartCommand() so this // method will not be called. @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); FlurryAgent.onStartSession(this, "MBY4JL18FQK1DPEJ5Y39"); Log.d(TAG, "Service Start"); Bundle bundle = intent.getExtras(); int id = bundle.getInt(Constraints.ID); profile = ProfileFactory.loadProfileFromDao(id); Log.d(TAG, profile.toString()); new Thread( new Runnable() { @Override public void run() { handler.sendEmptyMessage(MSG_CONNECT_START); isConnecting = true; enableDNSProxy = profile.isDNSProxy(); try { URL url = new URL("http://gae-ip-country.appspot.com/"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(2000); conn.setReadTimeout(5000); conn.connect(); InputStream is = conn.getInputStream(); BufferedReader input = new BufferedReader(new InputStreamReader(is)); String code = input.readLine(); if (code != null && code.length() > 0 && code.length() < 3) { Log.d(TAG, "Location: " + code); if (!code.contains("CN") && !code.contains("ZZ")) enableDNSProxy = false; } } catch (Exception e) { Log.d(TAG, "Cannot get country code"); // Nothing } if (enableDNSProxy) { if (dnsServer == null) { // dnsServer = new DNSServer("DNS Server", "8.8.4.4", // 53, // SSHTunnelService.this); dnsServer = new DNSServer("DNS Server", "127.0.0.1", 8053, SSHTunnelService.this); dnsServer.setBasePath("/data/data/org.sshtunnel"); dnsPort = dnsServer.init(); } } // Test for Redirect Support initHasRedirectSupported(); if (isOnline() && connect()) { // Connection and forward successful finishConnection(); if (enableDNSProxy) { // Start DNS Proxy Thread dnsThread = new Thread(dnsServer); dnsThread.setDaemon(true); dnsThread.start(); } notifyAlert( getString(R.string.forward_success), getString(R.string.service_running)); handler.sendEmptyMessage(MSG_CONNECT_FINISH); handler.sendEmptyMessage(MSG_CONNECT_SUCCESS); // for widget, maybe exception here try { RemoteViews views = new RemoteViews(getPackageName(), R.layout.sshtunnel_appwidget); views.setImageViewResource(R.id.serviceToggle, R.drawable.on); AppWidgetManager awm = AppWidgetManager.getInstance(SSHTunnelService.this); awm.updateAppWidget( awm.getAppWidgetIds( new ComponentName( SSHTunnelService.this, SSHTunnelWidgetProvider.class)), views); } catch (Exception ignore) { // Nothing } } else { // Connection or forward unsuccessful notifyAlert( getString(R.string.forward_fail) + ": " + reason, getString(R.string.service_failed), Notification.FLAG_AUTO_CANCEL); handler.sendEmptyMessage(MSG_CONNECT_FINISH); handler.sendEmptyMessage(MSG_CONNECT_FAIL); try { Thread.sleep(1000); } catch (InterruptedException ignore) { // Nothing } connected = false; stopSelf(); } isConnecting = false; } }) .start(); markServiceStarted(); }
/** * Internal method to request actual PTY terminal once we've finished authentication. If called * before authenticated, it will just fail. */ private void finishConnection() { Log.e(TAG, "Forward Successful"); if (profile.isSocks()) runRootCommand(BASE + "proxy_socks.sh start " + profile.getLocalPort()); else runRootCommand(BASE + "proxy_http.sh start " + profile.getLocalPort()); StringBuffer cmd = new StringBuffer(); cmd.append(BASE + "iptables -t nat -N SSHTUNNEL\n"); cmd.append(BASE + "iptables -t nat -F SSHTUNNEL\n"); if (enableDNSProxy) { cmd.append(BASE + "iptables -t nat -N SSHTUNNELDNS\n"); cmd.append(BASE + "iptables -t nat -F SSHTUNNELDNS\n"); if (hasRedirectSupport) cmd.append( BASE + "iptables " + "-t nat -A SSHTUNNELDNS -p udp --dport 53 -j REDIRECT --to " + dnsPort + "\n"); else cmd.append( BASE + "iptables " + "-t nat -A SSHTUNNELDNS -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:" + dnsPort + "\n"); cmd.append(BASE + "iptables -t nat -A OUTPUT -p udp -j SSHTUNNELDNS\n"); } if (profile.isSocks()) cmd.append( hasRedirectSupport ? CMD_IPTABLES_REDIRECT_ADD_SOCKS : CMD_IPTABLES_DNAT_ADD_SOCKS); else cmd.append(hasRedirectSupport ? CMD_IPTABLES_REDIRECT_ADD : CMD_IPTABLES_DNAT_ADD); cmd.append(CMD_IPTABLES_RETURN.replace("0.0.0.0", hostAddress)); if (profile.isGFWList()) { String[] gfw_list = getResources().getStringArray(R.array.gfw_list); for (String item : gfw_list) { cmd.append(BASE + "iptables -t nat -A OUTPUT -p tcp -d " + item + " -j SSHTUNNEL\n"); } } else if (profile.isAutoSetProxy()) { cmd.append(BASE + "iptables -t nat -A OUTPUT -p tcp -j SSHTUNNEL\n"); } else { // for proxy specified apps if (apps == null || apps.length <= 0) apps = AppManager.getProxyedApps(this, profile.getProxyedApps()); for (int i = 0; i < apps.length; i++) { if (apps[i].isProxyed()) { cmd.append( BASE + "iptables " + "-t nat -m owner --uid-owner " + apps[i].getUid() + " -A OUTPUT -p tcp -j SSHTUNNEL\n"); } } } String rules = cmd.toString(); if (hostAddress != null) rules = rules .replace("--dport 443", "! -d " + hostAddress + " --dport 443") .replace("--dport 80", "! -d " + hostAddress + " --dport 80"); if (profile.isSocks()) runRootCommand(rules.replace("8124", "8123")); else runRootCommand(rules); }
@Override public void connectionLost(Throwable reason) { Log.d(TAG, "Connection Lost"); SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); if (isConnecting || isStopping) { return; } if (!isOnline()) { stopReconnect(df); return; } if (reason != null) { if (reason.getMessage().contains("There was a problem during connect")) { Log.e(TAG, "connection lost", reason); return; } else if (reason.getMessage().contains("Closed due to user request")) { Log.e(TAG, "connection lost", reason); return; } else if (reason.getMessage().contains("The connect timeout expired")) { stopReconnect(df); return; } Log.e(TAG, "connection lost: " + reason.getMessage()); } else { stopReconnect(df); return; } if (profile.isAutoReconnect() && connected) { for (int reconNum = 1; reconNum <= RECONNECT_TRIES; reconNum++) { Log.d(TAG, "Reconnect tries: " + reconNum); onDisconnect(); if (!connect()) { try { Thread.sleep(2000 * reconNum); } catch (Exception ignore) { // Nothing } continue; } notifyAlert( getString(R.string.reconnect_success) + " " + df.format(new Date()), getString(R.string.reconnect_success)); return; } } stopReconnect(df); }
public boolean connect() { // try { // // connection.setCompression(true); // connection.setTCPNoDelay(true); // // } catch (IOException e) { // Log.e(TAG, "Could not enable compression!", e); // } // initialize the upstream proxy if (profile.isUpstreamProxy()) { try { if (profile.getUpstreamProxy() == null || profile.getUpstreamProxy().equals("")) throw new Exception(); String[] proxyInfo = profile.getUpstreamProxy().split("@"); if (proxyInfo.length == 1) { String[] hostInfo = proxyInfo[0].split(":"); if (hostInfo.length != 2) throw new Exception(); proxyData = new HTTPProxyData(hostInfo[0], Integer.valueOf(hostInfo[1])); } else if (proxyInfo.length == 2) { String[] userInfo = proxyInfo[0].split(":"); if (userInfo.length != 2) throw new Exception(); String[] hostInfo = proxyInfo[1].split(":"); if (hostInfo.length != 2) throw new Exception(); proxyData = new HTTPProxyData( hostInfo[0], Integer.valueOf(hostInfo[1]), userInfo[0], userInfo[1]); } else { throw new Exception(); } } catch (Exception e) { if (reason == null) reason = getString(R.string.upstream_format_error); return false; } } // get the host ip address if (proxyData != null) { try { hostAddress = InetAddress.getByName(proxyData.proxyHost).getHostAddress(); } catch (UnknownHostException e) { hostAddress = null; } } else { try { hostAddress = InetAddress.getByName(profile.getHost()).getHostAddress(); } catch (UnknownHostException e) { hostAddress = null; } } // fail to connect if the dns lookup failed if (hostAddress == null) { if (reason == null) reason = getString(R.string.fail_to_connect); return false; } // begin to connect try { connection = new Connection(profile.getHost(), profile.getPort()); if (proxyData != null) connection.setProxyData(proxyData); connection.addConnectionMonitor(this); /* * Uncomment when debugging SSH protocol: */ /* * DebugLogger logger = new DebugLogger() { * * public void log(int level, String className, String message) { * Log.d("SSH", message); } * * }; * * Logger.enabled = true; Logger.logger = logger; */ connection.connect(this, 10 * 1000, 20 * 1000); connected = true; } catch (Exception e) { Log.e(TAG, "Problem in SSH connection thread during connecting", e); // Display the reason in the text. if (reason == null) reason = getString(R.string.fail_to_connect); return false; } try { // enter a loop to keep trying until authentication int tries = 0; while (connected && !connection.isAuthenticationComplete() && tries++ < AUTH_TRIES) { authenticate(); // sleep to make sure we dont kill system Thread.sleep(1000); } } catch (Exception e) { Log.e(TAG, "Problem in SSH connection thread during authentication", e); if (reason == null) reason = getString(R.string.fail_to_authenticate); return false; } try { if (connection.isAuthenticationComplete()) { return enablePortForward(); } } catch (Exception e) { Log.e(TAG, "Problem in SSH connection thread during enabling port", e); if (reason == null) reason = getString(R.string.fail_to_connect); return false; } if (reason == null) reason = getString(R.string.fail_to_authenticate); Log.e(TAG, "Cannot authenticate"); return false; }
private void authenticate() { try { if (connection.authenticateWithNone(profile.getUser())) { Log.d(TAG, "Authenticate with none"); return; } } catch (Exception e) { Log.d(TAG, "Host does not support 'none' authentication."); } try { if (connection.isAuthMethodAvailable(profile.getUser(), AUTH_PUBLICKEY)) { File f = new File(profile.getKeyPath()); if (f.exists()) if (profile.getPassword().equals("")) profile.setPassword(null); if (connection.authenticateWithPublicKey(profile.getUser(), f, profile.getPassword())) { Log.d(TAG, "Authenticate with public key"); return; } } } catch (Exception e) { Log.d(TAG, "Host does not support 'Public key' authentication."); } try { if (connection.isAuthMethodAvailable(profile.getUser(), AUTH_PASSWORD)) { if (connection.authenticateWithPassword(profile.getUser(), profile.getPassword())) { Log.d(TAG, "Authenticate with password"); return; } } } catch (IllegalStateException e) { Log.e(TAG, "Connection went away while we were trying to authenticate", e); } catch (Exception e) { Log.e(TAG, "Problem during handleAuthentication()", e); } // TODO: Need verification try { if (connection.isAuthMethodAvailable(profile.getUser(), AUTH_KEYBOARDINTERACTIVE)) { if (connection.authenticateWithKeyboardInteractive(profile.getUser(), this)) return; } } catch (Exception e) { Log.d(TAG, "Host does not support 'Keyboard-Interactive' authentication."); } }