private void passTcpFileDescriptor(
     LocalSocket fdSocket,
     OutputStream outputStream,
     String socketId,
     String dstIp,
     int dstPort,
     int connectTimeout)
     throws Exception {
   Socket sock = new Socket();
   sock.setTcpNoDelay(true); // force file descriptor being created
   if (protect(sock)) {
     try {
       sock.connect(new InetSocketAddress(dstIp, dstPort), connectTimeout);
       ParcelFileDescriptor fd = ParcelFileDescriptor.fromSocket(sock);
       tcpSockets.put(socketId, sock);
       fdSocket.setFileDescriptorsForSend(new FileDescriptor[] {fd.getFileDescriptor()});
       outputStream.write('*');
       outputStream.flush();
       fd.detachFd();
     } catch (ConnectException e) {
       LogUtils.e("connect " + dstIp + ":" + dstPort + " failed");
       outputStream.write('!');
       sock.close();
     } catch (SocketTimeoutException e) {
       LogUtils.e("connect " + dstIp + ":" + dstPort + " failed");
       outputStream.write('!');
       sock.close();
     } finally {
       outputStream.flush();
     }
   } else {
     LogUtils.e("protect tcp socket failed");
   }
 }
 private void passFileDescriptor(LocalSocket fdSocket, FileDescriptor tunFD) throws Exception {
   OutputStream outputStream = fdSocket.getOutputStream();
   InputStream inputStream = fdSocket.getInputStream();
   try {
     BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream), 1);
     String request = reader.readLine();
     String[] parts = request.split(",");
     LogUtils.i("current open tcp sockets: " + tcpSockets.size());
     LogUtils.i("current open udp sockets: " + udpSockets.size());
     if ("TUN".equals(parts[0])) {
       fdSocket.setFileDescriptorsForSend(new FileDescriptor[] {tunFD});
       outputStream.write('*');
     } else if ("OPEN UDP".equals(parts[0])) {
       String socketId = parts[1];
       passUdpFileDescriptor(fdSocket, outputStream, socketId);
     } else if ("OPEN TCP".equals(parts[0])) {
       String socketId = parts[1];
       String dstIp = parts[2];
       int dstPort = Integer.parseInt(parts[3]);
       int connectTimeout = Integer.parseInt(parts[4]);
       passTcpFileDescriptor(fdSocket, outputStream, socketId, dstIp, dstPort, connectTimeout);
     } else if ("CLOSE UDP".equals(parts[0])) {
       String socketId = parts[1];
       DatagramSocket sock = udpSockets.get(socketId);
       if (sock != null) {
         udpSockets.remove(socketId);
         sock.close();
       }
     } else if ("CLOSE TCP".equals(parts[0])) {
       String socketId = parts[1];
       Socket sock = tcpSockets.get(socketId);
       if (sock != null) {
         tcpSockets.remove(socketId);
         sock.close();
       }
     } else {
       throw new UnsupportedOperationException("fdsock unable to handle: " + request);
     }
   } finally {
     try {
       inputStream.close();
     } catch (Exception e) {
       LogUtils.e("failed to close input stream", e);
     }
     try {
       outputStream.close();
     } catch (Exception e) {
       LogUtils.e("failed to close output stream", e);
     }
     fdSocket.close();
   }
 }
  private void listenFdServerSocket(final ParcelFileDescriptor tunPFD) throws Exception {
    final LocalServerSocket fdServerSocket = new LocalServerSocket("fdsock");
    try {

      ExecutorService executorService = Executors.newFixedThreadPool(16);
      while (isRunning()) {
        try {
          final LocalSocket fdSocket = fdServerSocket.accept();
          executorService.submit(
              new Runnable() {
                @Override
                public void run() {
                  try {
                    passFileDescriptor(fdSocket, tunPFD.getFileDescriptor());
                  } catch (Exception e) {
                    LogUtils.e("failed to handle fdsock", e);
                  }
                }
              });
        } catch (Exception e) {
          LogUtils.e("failed to handle fdsock", e);
        }
      }
      executorService.shutdown();
    } finally {
      fdServerSocket.close();
    }
  }
 static {
   try {
     SOCKS_VPN_SERVICE_CLASS =
         LaunchService.class.forName("fq.router.free_internet.SocksVpnService");
   } catch (ClassNotFoundException e) {
     LogUtils.e("failed to load SocksVpnService.class", e);
   }
 }
 public static boolean ping(boolean isVpnMode) {
   try {
     String content = HttpUtils.get("http://127.0.0.1:8318/ping");
     if (isVpnMode ? "VPN PONG".equals(content) : "PONG".equals(content)) {
       return true;
     } else {
       LogUtils.e("ping failed: " + content);
       return false;
     }
   } catch (HttpUtils.Error e) {
     LogUtils.e("ping failed: [" + e.responseCode + "] " + e.output);
     return false;
   } catch (Exception e) {
     LogUtils.e("ping failed: " + e);
     return false;
   }
 }
 @Override
 protected void onHandleIntent(Intent intent) {
   LogUtils.i("ver: " + getMyVersion(this));
   LogUtils.i("rooted: " + ShellUtils.checkRooted());
   if (isVpnRunning()) {
     LogUtils.i("manager is already running in vpn mode");
     sendBroadcast(new LaunchedIntent(true));
     return;
   }
   if (ping(false)) {
     LogUtils.i("manager is already running in root mode");
     sendBroadcast(new LaunchedIntent(false));
     return;
   }
   if (ping(true)) {
     LogUtils.i("Restart manager");
     try {
       ManagerProcess.kill();
       Thread.sleep(1000);
       if (ManagerProcess.exists()) {
         LogUtils.e("failed to restart manager", null);
       } else {
         LaunchService.execute(this);
       }
     } catch (Exception e) {
       handleFatalError(LogUtils.e("failed to stop exiting process", e));
     }
     return;
   }
   deployAndLaunch();
 }
 private Process executeManager(boolean isVpnMode) throws Exception {
   Map<String, String> env =
       new HashMap<String, String>() {
         {
           put("FQROUTER_VERSION", getMyVersion(LaunchService.this));
         }
       };
   env.putAll(ShellUtils.pythonEnv());
   if (isVpnMode) {
     Process process =
         ShellUtils.executeNoWait(env, ShellUtils.BUSYBOX_FILE.getCanonicalPath(), "sh");
     OutputStreamWriter stdin = new OutputStreamWriter(process.getOutputStream());
     try {
       String command =
           Deployer.PYTHON_LAUNCHER
               + " "
               + Deployer.MANAGER_VPN_PY.getAbsolutePath()
               + " > /data/data/fq.router/log/current-python.log 2>&1";
       LogUtils.i("write to stdin: " + command);
       stdin.write(command);
       stdin.write("\nexit\n");
     } finally {
       stdin.close();
     }
     return process;
   } else {
     try {
       LogUtils.i(
           ShellUtils.sudo(
                   env,
                   Deployer.PYTHON_LAUNCHER
                       + " -c \"import os; print 'current user id: %s' % os.getuid()\"")
               .trim());
     } catch (Exception e) {
       LogUtils.e("log current user id failed", e);
     }
     return ShellUtils.sudoNoWait(
         env,
         Deployer.PYTHON_LAUNCHER
             + " "
             + Deployer.MANAGER_MAIN_PY.getAbsolutePath()
             + " > /data/data/fq.router/log/current-python.log 2>&1");
   }
 }
 public static String getMyVersion(Context context) {
   try {
     PackageInfo packageInfo =
         context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
     return packageInfo.versionName;
   } catch (Exception e) {
     LogUtils.e("failed to get package info", e);
     return "Unknown";
   }
 }
 private void stopVpn() {
   try {
     tunPFD.close();
   } catch (IOException e) {
     LogUtils.e("failed to stop tunPFD", e);
   }
   tunPFD = null;
   MainActivity.setShouldExit();
   ExitService.execute(this);
 }
 private void passUdpFileDescriptor(
     LocalSocket fdSocket, OutputStream outputStream, String socketId) throws Exception {
   DatagramSocket sock = new DatagramSocket();
   if (protect(sock)) {
     ParcelFileDescriptor fd = ParcelFileDescriptor.fromDatagramSocket(sock);
     udpSockets.put(socketId, sock);
     fdSocket.setFileDescriptorsForSend(new FileDescriptor[] {fd.getFileDescriptor()});
     outputStream.write('*');
     outputStream.flush();
     fd.detachFd();
   } else {
     LogUtils.e("protect udp socket failed");
   }
 }
 private void deployAndLaunch() {
   try {
     LogUtils.i("Kill existing manager process");
     LogUtils.i("try to kill manager process before launch");
     ManagerProcess.kill();
   } catch (Exception e) {
     LogUtils.e("failed to kill manager process before launch", e);
     LogUtils.i("failed to kill manager process before launch");
   }
   Deployer deployer = new Deployer(this);
   String fatalError = deployer.deploy();
   if (!fatalError.isEmpty()) {
     handleFatalError(fatalError);
     return;
   }
   updateConfigFile(this);
   LogUtils.i("Launching...");
   if (ShellUtils.checkRooted()) {
     fatalError = launch(false);
     if (fatalError.isEmpty()) {
       sendBroadcast(new LaunchedIntent(false));
     } else {
       handleFatalError(fatalError);
     }
   } else {
     if (Build.VERSION.SDK_INT < 14) {
       handleFatalError("[ROOT] is required");
       return;
     }
     fatalError = launch(true);
     if (fatalError.isEmpty()) {
       sendBroadcast(new LaunchedIntent(true));
     } else {
       handleFatalError(fatalError);
     }
   }
 }
 private void startVpn() {
   try {
     if (tunPFD != null) {
       throw new RuntimeException("another VPN is still running");
     }
     tunPFD =
         new Builder()
             .setSession("fqrouter")
             .addAddress("10.25.1.1", 24)
             .addRoute("0.0.0.0", 0)
             .addDnsServer("8.8.8.8")
             .establish();
     if (tunPFD == null) {
       stopSelf();
       return;
     }
     final int tunFD = tunPFD.getFd();
     LogUtils.i("tunFD is " + tunFD);
     new Thread(
             new Runnable() {
               @Override
               public void run() {
                 try {
                   listenFdServerSocket(tunPFD);
                 } catch (Exception e) {
                   LogUtils.e("fdsock failed " + e, e);
                 }
               }
             })
         .start();
     updateStatus("Started in VPN mode");
     sendBroadcast(new LaunchedIntent(true));
   } catch (Exception e) {
     LogUtils.e("establish failed", e);
   }
 }
 private String launch(boolean isVpnMode) {
   try {
     Process process = executeManager(isVpnMode);
     for (int i = 0; i < 30; i++) {
       if (ping(isVpnMode)) {
         return "";
       }
       if (hasProcessExited(process)) {
         return "manager quit";
       }
       sleepOneSecond();
     }
     return "timed out";
   } catch (Exception e) {
     return LogUtils.e("failed to launch", e);
   }
 }
 public static void updateConfigFile(Context context) {
   try {
     SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
     JSONObject configJson = new JSONObject();
     configJson.put("wifi_hotspot_ssid", preferences.getString("WifiHotspotSSID", "fqrouter"));
     configJson.put(
         "wifi_hotspot_password", preferences.getString("WifiHotspotPassword", "p@55word"));
     configJson.put("tcp_scrambler_enabled", preferences.getBoolean("TcpScramblerEnabled", true));
     configJson.put(
         "youtube_scrambler_enabled", preferences.getBoolean("YoutubeScramblerEnabled", true));
     configJson.put(
         "goagent_public_servers_enabled",
         preferences.getBoolean("GoAgentPublicServersEnabled", true));
     configJson.put(
         "shadowsocks_public_servers_enabled",
         preferences.getBoolean("ShadowsocksPublicServersEnabled", true));
     configJson.put(
         "http_proxy_public_servers_enabled",
         preferences.getBoolean("HttpProxyPublicServersEnabled", true));
     IOUtils.writeToFile(FQROUTER_CONFIG_FILE, configJson.toString());
   } catch (Exception e) {
     LogUtils.e("failed to update config file", e);
   }
 }
 @Override
 public void onStart(Intent intent, int startId) {
   startVpn();
   LogUtils.i("on start");
 }