326 lines
9.1 KiB
Java
326 lines
9.1 KiB
Java
package org.torproject.android.service;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import org.json.JSONArray;
|
|
import org.json.JSONObject;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.Proxy;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.text.NumberFormat;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.StringTokenizer;
|
|
|
|
|
|
import org.torproject.android.control.EventHandler;
|
|
import org.torproject.android.service.util.Prefs;
|
|
|
|
/**
|
|
* Created by n8fr8 on 9/25/16.
|
|
*/
|
|
public class TorEventHandler implements EventHandler, TorServiceConstants {
|
|
|
|
private TorService mService;
|
|
|
|
|
|
private long lastRead = -1;
|
|
private long lastWritten = -1;
|
|
private long mTotalTrafficWritten = 0;
|
|
private long mTotalTrafficRead = 0;
|
|
|
|
private NumberFormat mNumberFormat = null;
|
|
|
|
|
|
private HashMap<String,Node> hmBuiltNodes = new HashMap<String,Node>();
|
|
|
|
public class Node
|
|
{
|
|
String status;
|
|
String id;
|
|
String name;
|
|
String ipAddress;
|
|
String country;
|
|
String organization;
|
|
}
|
|
|
|
public HashMap<String,Node> getNodes ()
|
|
{
|
|
return hmBuiltNodes;
|
|
}
|
|
|
|
public TorEventHandler (TorService service)
|
|
{
|
|
mService = service;
|
|
mNumberFormat = NumberFormat.getInstance(Locale.getDefault()); //localized numbers!
|
|
|
|
}
|
|
|
|
@Override
|
|
public void message(String severity, String msg) {
|
|
mService.logNotice(severity + ": " + msg);
|
|
}
|
|
|
|
@Override
|
|
public void newDescriptors(List<String> orList) {
|
|
}
|
|
|
|
@Override
|
|
public void orConnStatus(String status, String orName) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("orConnStatus (");
|
|
sb.append(parseNodeName(orName) );
|
|
sb.append("): ");
|
|
sb.append(status);
|
|
|
|
mService.debug(sb.toString());
|
|
}
|
|
|
|
@Override
|
|
public void streamStatus(String status, String streamID, String target) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("StreamStatus (");
|
|
sb.append((streamID));
|
|
sb.append("): ");
|
|
sb.append(status);
|
|
|
|
mService.logNotice(sb.toString());
|
|
}
|
|
|
|
@Override
|
|
public void unrecognized(String type, String msg) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Message (");
|
|
sb.append(type);
|
|
sb.append("): ");
|
|
sb.append(msg);
|
|
|
|
mService.logNotice(sb.toString());
|
|
}
|
|
|
|
@Override
|
|
public void bandwidthUsed(long read, long written) {
|
|
|
|
if (read != lastRead || written != lastWritten)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(formatCount(read));
|
|
sb.append(" \u2193");
|
|
sb.append(" / ");
|
|
sb.append(formatCount(written));
|
|
sb.append(" \u2191");
|
|
|
|
int iconId = R.drawable.ic_stat_tor;
|
|
|
|
if (read > 0 || written > 0)
|
|
iconId = R.drawable.ic_stat_tor_xfer;
|
|
|
|
if (mService.hasConnectivity() && Prefs.expandedNotifications())
|
|
mService.showToolbarNotification(sb.toString(), mService.getNotifyId(), iconId);
|
|
|
|
mTotalTrafficWritten += written;
|
|
mTotalTrafficRead += read;
|
|
}
|
|
|
|
lastWritten = written;
|
|
lastRead = read;
|
|
|
|
mService.sendCallbackBandwidth(lastWritten, lastRead, mTotalTrafficWritten, mTotalTrafficRead);
|
|
}
|
|
|
|
private String formatCount(long count) {
|
|
// Converts the supplied argument into a string.
|
|
|
|
// Under 2Mb, returns "xxx.xKb"
|
|
// Over 2Mb, returns "xxx.xxMb"
|
|
if (mNumberFormat != null)
|
|
if (count < 1e6)
|
|
return mNumberFormat.format(Math.round((float)((int)(count*10/1024))/10)) + "kbps";
|
|
else
|
|
return mNumberFormat.format(Math.round((float)((int)(count*100/1024/1024))/100)) + "mbps";
|
|
else
|
|
return "";
|
|
|
|
//return count+" kB";
|
|
}
|
|
|
|
public void circuitStatus(String status, String circID, String path) {
|
|
|
|
/* once the first circuit is complete, then announce that Orbot is on*/
|
|
if (mService.getCurrentStatus() == STATUS_STARTING && TextUtils.equals(status, "BUILT"))
|
|
mService.sendCallbackStatus(STATUS_ON);
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Circuit (");
|
|
sb.append((circID));
|
|
sb.append(") ");
|
|
sb.append(status);
|
|
sb.append(": ");
|
|
|
|
StringTokenizer st = new StringTokenizer(path,",");
|
|
Node node = null;
|
|
|
|
while (st.hasMoreTokens())
|
|
{
|
|
String nodePath = st.nextToken();
|
|
node = new Node();
|
|
|
|
String[] nodeParts;
|
|
|
|
if (nodePath.contains("="))
|
|
nodeParts = nodePath.split("=");
|
|
else
|
|
nodeParts = nodePath.split("~");
|
|
|
|
if (nodeParts.length == 1)
|
|
{
|
|
node.id = nodeParts[0].substring(1);
|
|
node.name = node.id;
|
|
}
|
|
else if (nodeParts.length == 2)
|
|
{
|
|
node.id = nodeParts[0].substring(1);
|
|
node.name = nodeParts[1];
|
|
}
|
|
|
|
node.status = status;
|
|
|
|
sb.append(node.name);
|
|
|
|
if (st.hasMoreTokens())
|
|
sb.append (" > ");
|
|
}
|
|
|
|
if (Prefs.useDebugLogging())
|
|
mService.debug(sb.toString());
|
|
else if(status.equals("BUILT"))
|
|
mService.logNotice(sb.toString());
|
|
else if (status.equals("CLOSED"))
|
|
mService.logNotice(sb.toString());
|
|
|
|
if (Prefs.expandedNotifications())
|
|
{
|
|
//get IP from last nodename
|
|
if(status.equals("BUILT")){
|
|
|
|
if (node.ipAddress == null)
|
|
mService.exec(new ExternalIPFetcher(node));
|
|
|
|
hmBuiltNodes.put(circID, node);
|
|
}
|
|
|
|
if (status.equals("CLOSED"))
|
|
{
|
|
hmBuiltNodes.remove(circID);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private class ExternalIPFetcher implements Runnable {
|
|
|
|
private Node mNode;
|
|
private int MAX_ATTEMPTS = 3;
|
|
private final static String ONIONOO_BASE_URL = "https://onionoo.torproject.org/details?fields=country_name,as_name,or_addresses&lookup=";
|
|
|
|
public ExternalIPFetcher (Node node)
|
|
{
|
|
mNode = node;
|
|
}
|
|
|
|
public void run ()
|
|
{
|
|
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++)
|
|
{
|
|
if (mService.getControlConnection() != null)
|
|
{
|
|
try {
|
|
|
|
URLConnection conn = null;
|
|
|
|
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8118));
|
|
conn = new URL(ONIONOO_BASE_URL + mNode.id).openConnection(proxy);
|
|
|
|
conn.setRequestProperty("Connection","Close");
|
|
conn.setConnectTimeout(60000);
|
|
conn.setReadTimeout(60000);
|
|
|
|
InputStream is = conn.getInputStream();
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
|
|
|
// getting JSON string from URL
|
|
|
|
StringBuffer json = new StringBuffer();
|
|
String line = null;
|
|
|
|
while ((line = reader.readLine())!=null)
|
|
json.append(line);
|
|
|
|
JSONObject jsonNodeInfo = new org.json.JSONObject(json.toString());
|
|
|
|
JSONArray jsonRelays = jsonNodeInfo.getJSONArray("relays");
|
|
|
|
if (jsonRelays.length() > 0)
|
|
{
|
|
mNode.ipAddress = jsonRelays.getJSONObject(0).getJSONArray("or_addresses").getString(0).split(":")[0];
|
|
mNode.country = jsonRelays.getJSONObject(0).getString("country_name");
|
|
mNode.organization = jsonRelays.getJSONObject(0).getString("as_name");
|
|
|
|
StringBuffer sbInfo = new StringBuffer();
|
|
sbInfo.append(mNode.ipAddress);
|
|
|
|
if (mNode.country != null)
|
|
sbInfo.append(' ').append(mNode.country);
|
|
|
|
if (mNode.organization != null)
|
|
sbInfo.append(" (").append(mNode.organization).append(')');
|
|
|
|
mService.logNotice(sbInfo.toString());
|
|
|
|
}
|
|
|
|
reader.close();
|
|
is.close();
|
|
|
|
break;
|
|
|
|
} catch (Exception e) {
|
|
|
|
mService.debug ("Error getting node details from onionoo: " + e.getMessage());
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private String parseNodeName(String node)
|
|
{
|
|
if (node.indexOf('=')!=-1)
|
|
{
|
|
return (node.substring(node.indexOf("=")+1));
|
|
}
|
|
else if (node.indexOf('~')!=-1)
|
|
{
|
|
return (node.substring(node.indexOf("~")+1));
|
|
}
|
|
else
|
|
return node;
|
|
}
|
|
}
|