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"); }