/* (C) 2012 Pragmatic Software This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/ */ package com.googlecode.networklog; import android.content.Context; import android.content.res.Resources; import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Iptables { public static HashMap<String, String> targets = null; public static boolean getTargets(Context context) { if(targets != null) { return true; } targets = new HashMap<String, String>(); if(!NetworkLog.shell.sendCommand("cat /proc/net/ip_tables_targets")) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_check_rules), NetworkLog.shell.getError(true)); return false; } List<String> output = new ArrayList<String>(); if(NetworkLog.shell.waitForCommandExit(output) != 0) { String error = ""; for(String line : output) { error += line; } Log.e("NetworkLog", "Bad exit for getTargets (exit " + NetworkLog.shell.exitval + ")"); SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_check_rules), error); return false; } StringBuilder result = new StringBuilder(); for(String line : output) { line = line.trim(); targets.put(line, line); result.append(line).append(" "); } MyLog.d("getTargets result: [" + result + "]"); return true; } public static boolean addRules(Context context) { String iptablesBinary = SysUtils.getIptablesBinary(context); if(iptablesBinary == null) { return false; } if(targets == null && getTargets(context) == false) { return false; } if(!removeRules(context)) { return false; } if (targets.get("LOG") == null && targets.get("NFLOG") == null) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_unsupported_title), context.getResources().getString(R.string.iptables_error_missingfeatures_text)); return false; } ArrayList<String> commands = new ArrayList<String>(); commands.add(iptablesBinary + " -N NetworkLog"); if(NetworkLogService.behindFirewall) { commands.add(iptablesBinary + " -A OUTPUT ! -o lo -j NetworkLog"); commands.add(iptablesBinary + " -A INPUT ! -i lo -j NetworkLog"); } else { commands.add(iptablesBinary + " -I OUTPUT ! -o lo -j NetworkLog"); commands.add(iptablesBinary + " -I INPUT ! -i lo -j NetworkLog"); } if(targets.get("LOG") != null) { commands.add(iptablesBinary + " -A NetworkLog -j LOG --log-prefix \"{NL}\" --log-uid"); } else if(targets.get("NFLOG") != null) { commands.add(iptablesBinary + " -A NetworkLog -j NFLOG --nflog-prefix \"{NL}\""); } for(String command : commands) { if(!NetworkLog.shell.sendCommand(command)) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_add_rules), NetworkLog.shell.getError(true)); return false; } List<String> output = new ArrayList<String>(); NetworkLog.shell.waitForCommandExit(output); StringBuilder result = new StringBuilder(); for(String line : output) { result.append(line); } if(MyLog.enabled) { MyLog.d("addRules result: [" + result + "]"); } if(NetworkLog.shell.exitval != 0) { Log.e("NetworkLog", "Bad exit for addRules (exit " + NetworkLog.shell.exitval + ")"); SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_add_rules), result.toString()); return false; } if(result.indexOf("No chain/target/match by that name", 0) != -1) { Resources res = context.getResources(); SysUtils.showError(context, res.getString(R.string.iptables_error_unsupported_title), res.getString(R.string.iptables_error_missingfeatures_text)); return false; } } for (Map.Entry<String, String> entry : NetworkLogService.blockedApps.entrySet()) { Integer appId = ApplicationsTracker.packageMap.get(entry.getValue()).uid; ignoreApp(context, appId); } return true; } public static boolean removeRules(Context context) { String iptablesBinary = SysUtils.getIptablesBinary(context); if(iptablesBinary == null) { return false; } if(targets == null && getTargets(context) == false) { return false; } if (targets.get("LOG") == null && targets.get("NFLOG") == null) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_unsupported_title), context.getResources().getString(R.string.iptables_error_missingfeatures_text)); return false; } int tries = 0; while(checkRules(context) == true) { ArrayList<String> commands = new ArrayList<String>(); commands.add(iptablesBinary + " -D INPUT ! -i lo -j NetworkLog"); commands.add(iptablesBinary + " -D OUTPUT ! -o lo -j NetworkLog"); commands.add(iptablesBinary + " -F NetworkLog"); commands.add(iptablesBinary + " -X NetworkLog"); for(String command : commands) { if(!NetworkLog.shell.sendCommand(command)) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_remove_rules), NetworkLog.shell.getError(true)); return false; } List<String> output = new ArrayList<String>(); NetworkLog.shell.waitForCommandExit(output); StringBuilder result = new StringBuilder(); for(String line : output) { result.append(line); } if(MyLog.enabled) { MyLog.d("removeRules result: [" + result + "]"); } if(NetworkLog.shell.exitval != 0) { Log.e("NetworkLog", "Bad exit for removeRules (exit " + NetworkLog.shell.exitval + ")"); SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_remove_rules), result.toString()); return false; } } tries++; if(tries > 3) { Log.w("NetworkLog", "Too many attempts to remove rules, moving along..."); return false; } } return true; } public static String getRules(Context context) { return getRules(context, false); } public static String getRules(Context context, boolean verbose) { String iptablesBinary = SysUtils.getIptablesBinary(context); if(iptablesBinary == null) { return null; } String command; if(verbose) { command = iptablesBinary + " -L -v"; } else { command = iptablesBinary + " -L"; } if(!NetworkLog.shell.sendCommand(command)) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_check_rules), NetworkLog.shell.getError(true)); return null; } List<String> output = new ArrayList<String>(); NetworkLog.shell.waitForCommandExit(output); StringBuilder result = new StringBuilder(); for(String line : output) { result.append(line); } if(MyLog.enabled) { MyLog.d("getRules result: [" + result + "]"); } if(NetworkLog.shell.exitval != 0) { Log.e("NetworkLog", "Bad exit for getRules (exit " + NetworkLog.shell.exitval + ")"); SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_check_rules), result.toString()); return null; } return result.toString(); } public static boolean checkRules(Context context) { String rules = getRules(context, true); if(rules == null) { return false; } if(rules.indexOf("Perhaps iptables or your kernel needs to be upgraded", 0) != -1) { Resources res = context.getResources(); SysUtils.showError(context, res.getString(R.string.iptables_error_unsupported_title), res.getString(R.string.iptables_error_unsupported_text)); return false; } return rules.indexOf("{NL}", 0) == -1 ? false : true; } public static boolean ignoreApp(Context context, Integer appId) { String iptablesBinary = SysUtils.getIptablesBinary(context); if(iptablesBinary == null) { return false; } if(!NetworkLog.shell.sendCommand(iptablesBinary + " -I NetworkLog -m owner --uid-owner " + appId + " -j RETURN")) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_add_rules), NetworkLog.shell.getError(true)); return false; } List<String> output = new ArrayList<String>(); NetworkLog.shell.waitForCommandExit(output); StringBuilder result = new StringBuilder(); for(String line : output) { result.append(line); } if(MyLog.enabled) { MyLog.d("ignoreApp result: [" + result + "]"); } if(NetworkLog.shell.exitval != 0) { Log.e("NetworkLog", "Bad exit for ignoreApp (exit " + NetworkLog.shell.exitval + ")"); SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_add_rules), result.toString()); return false; } return true; } public static boolean unignoreApp(Context context, Integer appId) { String iptablesBinary = SysUtils.getIptablesBinary(context); if(iptablesBinary == null) { return false; } if(!NetworkLog.shell.sendCommand(iptablesBinary + " -D NetworkLog -m owner --uid-owner " + appId + " -j RETURN")) { SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_add_rules), NetworkLog.shell.getError(true)); return false; } List<String> output = new ArrayList<String>(); NetworkLog.shell.waitForCommandExit(output); StringBuilder result = new StringBuilder(); for(String line : output) { result.append(line); } if(MyLog.enabled) { MyLog.d("unignoreApp result: [" + result + "]"); } if(NetworkLog.shell.exitval != 0) { Log.e("NetworkLog", "Bad exit for unignoreApp (exit " + NetworkLog.shell.exitval + ")"); SysUtils.showError(context, context.getResources().getString(R.string.iptables_error_add_rules), result.toString()); return false; } return true; } }