예제 #1
0
  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;
  }
예제 #2
0
  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");
  }
예제 #3
0
 // 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;
 }
예제 #4
0
  @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;
  }
예제 #5
0
  // 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();
  }
예제 #6
0
  /**
   * 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);
  }
예제 #7
0
  @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);
  }
예제 #8
0
  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;
  }
예제 #9
0
  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.");
    }
  }