diff options
author | Andrew Comminos <andrewcomminos@gmail.com> | 2013-08-08 11:43:31 +0400 |
---|---|---|
committer | Andrew Comminos <andrewcomminos@gmail.com> | 2013-08-08 11:43:31 +0400 |
commit | f76b49c3e8072795e13f6bd4fb1634ab38514e10 (patch) | |
tree | 96165966b06ca387fc1f978ba92c4067ddea6d26 /src/main/java | |
parent | 9808e0611e413985e1ee7afeb0e0b51b371224c3 (diff) |
Added initial Tor support, as well as various user and channel tweaks.
Diffstat (limited to 'src/main/java')
7 files changed, 101 insertions, 35 deletions
diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java index f4c6d17..e458fd5 100644 --- a/src/main/java/com/morlunk/jumble/JumbleService.java +++ b/src/main/java/com/morlunk/jumble/JumbleService.java @@ -57,6 +57,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon public static final String EXTRAS_TRANSMIT_MODE = "transmit_mode"; public static final String EXTRAS_USE_OPUS = "use_opus"; public static final String EXTRAS_FORCE_TCP = "force_tcp"; + public static final String EXTRAS_USE_TOR = "use_tor"; public static final String EXTRAS_CLIENT_NAME = "client_name"; public static final String ACTION_DISCONNECT = "com.morlunk.jumble.DISCONNECT"; @@ -71,6 +72,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon public int mTransmitMode; public boolean mUseOpus; public boolean mForceTcp; + public boolean mUseTor; public String mClientName; private JumbleConnection mConnection; @@ -274,7 +276,8 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mDetectionThreshold = extras.getInt(EXTRAS_DETECTION_THRESHOLD, 1400); mTransmitMode = extras.getInt(EXTRAS_TRANSMIT_MODE, Constants.TRANSMIT_VOICE_ACTIVITY); mUseOpus = extras.getBoolean(EXTRAS_USE_OPUS, true); - mForceTcp = extras.getBoolean(EXTRAS_FORCE_TCP, false); + mUseTor = extras.getBoolean(EXTRAS_USE_TOR, false); + mForceTcp = extras.getBoolean(EXTRAS_FORCE_TCP, false) || mUseTor; // Tor requires TCP connections to work- if it's on, force TCP. mClientName = extras.containsKey(EXTRAS_CLIENT_NAME) ? extras.getString(EXTRAS_CLIENT_NAME) : "Jumble"; connect(); } @@ -299,7 +302,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon public void connect() { try { - mConnection = new JumbleConnection(this, this, mServer, mClientName, mCertificate, mCertificatePassword, mForceTcp, mUseOpus); + mConnection = new JumbleConnection(this, this, mServer, mClientName, mCertificate, mCertificatePassword, mForceTcp, mUseOpus, mUseTor); } catch (final JumbleConnectionException e) { e.printStackTrace(); diff --git a/src/main/java/com/morlunk/jumble/model/Channel.java b/src/main/java/com/morlunk/jumble/model/Channel.java index 76c93c3..300e28e 100644 --- a/src/main/java/com/morlunk/jumble/model/Channel.java +++ b/src/main/java/com/morlunk/jumble/model/Channel.java @@ -20,11 +20,13 @@ import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public final class Channel implements Parcelable { private int mId; private int mPosition; + private int mLevel; private boolean mTemporary; private int mParent = -1; private String mName; @@ -63,6 +65,7 @@ public final class Channel implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(mId); out.writeInt(mPosition); + out.writeInt(mLevel); out.writeValue(mTemporary); out.writeInt(mParent); out.writeString(mName); @@ -77,6 +80,7 @@ public final class Channel implements Parcelable { public void readFromParcel(Parcel in) { mId = in.readInt(); mPosition = in.readInt(); + mLevel = in.readInt(); mTemporary = (Boolean)in.readValue(null); mParent = in.readInt(); mName = in.readString(); @@ -94,7 +98,7 @@ public final class Channel implements Parcelable { } public void addUser(int userId) { - mUsers.add(userId); // TODO sorting + mUsers.add(userId); } public void removeUser(int userId) { @@ -161,6 +165,18 @@ public final class Channel implements Parcelable { this.mDescriptionHash = mDescriptionHash; } + public List<Integer> getSubchannels() { + return mSubchannels; + } + + public void addSubchannel(int channelId) { + mSubchannels.add(channelId); + } + + public void removeSubchannel(int channelId) { + mSubchannels.remove((Object)channelId); + } + public List<Integer> getLinks() { return mLinks; } diff --git a/src/main/java/com/morlunk/jumble/net/ChannelHandler.java b/src/main/java/com/morlunk/jumble/net/ChannelHandler.java index ed281a5..2457311 100644 --- a/src/main/java/com/morlunk/jumble/net/ChannelHandler.java +++ b/src/main/java/com/morlunk/jumble/net/ChannelHandler.java @@ -24,32 +24,32 @@ import com.morlunk.jumble.model.Channel; import com.morlunk.jumble.protobuf.Mumble; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; /** * Created by andrew on 18/07/13. */ public class ChannelHandler extends JumbleMessageHandler.Stub { - /** - * Channel comparator that first sorts by position, then alphabetical order. - */ - class ChannelSortComparator implements Comparator<Integer> { - + private Comparator<Integer> mChannelComparator = new Comparator<Integer>() { @Override public int compare(Integer lhs, Integer rhs) { - Channel clhs = mChannels.get(lhs); - Channel crhs = mChannels.get(rhs); - if(clhs.getPosition() > clhs.getPosition()) return 1; - else if(clhs.getPosition() < clhs.getPosition()) return -1; + Channel clhs = getChannel(lhs); + Channel crhs = getChannel(rhs); + if(clhs.getPosition() != crhs.getPosition()) + return ((Integer)clhs.getPosition()).compareTo(crhs.getPosition()); return clhs.getName().compareTo(crhs.getName()); } - } + }; private JumbleService mService; - private HashMap<Integer, Channel> mChannels = new HashMap<Integer, Channel>(); + private Map<Integer, Channel> mChannels = new HashMap<Integer, Channel>(); public ChannelHandler(JumbleService service) { mService = service; @@ -78,21 +78,27 @@ public class ChannelHandler extends JumbleMessageHandler.Stub { mChannels.put(msg.getChannelId(), channel); } - if(msg.hasParent()) - channel.setParent(parent.getId()); - if(msg.hasName()) channel.setName(msg.getName()); + if(msg.hasPosition()) + channel.setPosition(msg.getPosition()); + + if(msg.hasParent()) { + Channel oldParent = mChannels.get(channel.getParent()); + channel.setParent(parent.getId()); + parent.addSubchannel(channel.getId()); + Collections.sort(parent.getSubchannels(), mChannelComparator); // Re-sort after subchannel addition + if(oldParent != null) + oldParent.removeSubchannel(channel.getId()); + } + if(msg.hasDescriptionHash()) channel.setDescriptionHash(msg.getDescriptionHash().toByteArray()); if(msg.hasDescription()) channel.setDescription(msg.getDescription()); - if(msg.hasPosition()) - channel.setPosition(msg.getPosition()); - if(msg.getLinksCount() > 0) { channel.clearLinks(); for(int link : msg.getLinksList()) diff --git a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java index d3a055b..fc9400c 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java @@ -28,6 +28,7 @@ import com.google.protobuf.Message; import com.morlunk.jumble.Constants; import com.morlunk.jumble.model.Server; import com.morlunk.jumble.protobuf.Mumble; + import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; @@ -43,6 +44,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import socks.Socks5Proxy; +import socks.SocksSocket; + public class JumbleConnection { public interface JumbleConnectionListener { public void onConnectionEstablished(); @@ -51,6 +55,10 @@ public class JumbleConnection { public void onConnectionWarning(String warning); } + // Tor connection details + public static final String TOR_HOST = "localhost"; + public static final int TOR_PORT = 9050; + private Context mContext; private JumbleConnectionListener mListener; private String mClientName; @@ -69,8 +77,9 @@ public class JumbleConnection { private InetAddress mHost; private JumbleTCP mTCP; private JumbleUDP mUDP; - private boolean mForceTCP = false; + private boolean mForceTCP; private boolean mUseOpus = true; + private boolean mUseTor; private boolean mUsingUDP = true; private boolean mConnected; private CryptState mCryptState = new CryptState(); @@ -261,7 +270,8 @@ public class JumbleConnection { byte[] certificate, String certificatePassword, boolean forceTcp, - boolean useOpus) throws JumbleConnectionException { + boolean useOpus, + boolean useTor) throws JumbleConnectionException { mContext = context; mListener = listener; @@ -269,6 +279,7 @@ public class JumbleConnection { mClientName = clientName; mForceTCP = forceTcp; mUseOpus = useOpus; + mUseTor = useTor; mMainHandler = new Handler(context.getMainLooper()); mHandlers.add(mConnectionMessageHandler); setupSocketFactory(certificate, certificatePassword); @@ -371,6 +382,7 @@ public class JumbleConnection { * @param e The exception that caused termination. */ private void handleFatalException(final JumbleConnectionException e) { + e.printStackTrace(); if(mListener != null) { mMainHandler.post(new Runnable() { @Override @@ -396,6 +408,7 @@ public class JumbleConnection { ByteArrayInputStream inputStream = new ByteArrayInputStream(certificate); keyStore.load(inputStream, certificatePassword != null ? certificatePassword.toCharArray() : new char[0]); } + mSocketFactory = new JumbleSSLSocketFactory(keyStore, certificatePassword); } catch (KeyManagementException e) { throw new JumbleConnectionException("Could not recover keys from certificate", e, false); @@ -407,14 +420,14 @@ public class JumbleConnection { throw new JumbleConnectionException("Could not read certificate file", e, false); } catch (CertificateException e) { e.printStackTrace(); - }catch (NoSuchProviderException e) { + } catch (NoSuchAlgorithmException e) { /* * This will actually NEVER occur. * We use Spongy Castle to provide the algorithm and provider implementations. * There's no platform dependency. */ throw new RuntimeException("We use Spongy Castle- what? ", e); - } catch (NoSuchAlgorithmException e) { + } catch (NoSuchProviderException e) { /* * This will actually NEVER occur. * We use Spongy Castle to provide the algorithm and provider implementations. @@ -481,7 +494,8 @@ public class JumbleConnection { * @param data Raw protobuf TCP data. * @param messageType Type of the message. * @return The parsed protobuf message. - * @throws InvalidProtocolBufferException Called if the messageType does not match the data. + * @throws InvalidProtocolBufferException Called if the messageType doe + }s not match the data. */ public static final Message getProtobufMessage(byte[] data, JumbleTCPMessageType messageType) throws InvalidProtocolBufferException { switch (messageType) { @@ -680,13 +694,15 @@ public class JumbleConnection { handleFatalException(new JumbleConnectionException("Could not resolve host", e, true)); } - mTCPSocket = (SSLSocket)mSocketFactory.createSocket(); - mTCPSocket.setKeepAlive(true); - mTCPSocket.setEnabledProtocols(new String[] {"TLSv1"}); - mTCPSocket.setUseClientMode(true); - HttpParams httpParams = new BasicHttpParams(); - mSocketFactory.connectSocket(mTCPSocket, mServer.getHost(), mServer.getPort(), null, 0, httpParams); + + if(mUseTor) { + Socks5Proxy proxy = new Socks5Proxy(TOR_HOST, TOR_PORT); + proxy.resolveAddrLocally(false); // Tor requirement for SOCKS5 is to let it resolve the host. + SocksSocket proxySocket = new SocksSocket(proxy, mServer.getHost(), mServer.getPort()); + mTCPSocket = (SSLSocket) mSocketFactory.connectSocket(proxySocket, mServer.getHost(), mServer.getPort(), null, 0, httpParams); + } else + mTCPSocket = (SSLSocket) mSocketFactory.connectSocket(null, mServer.getHost(), mServer.getPort(), null, 0, httpParams); mTCPSocket.startHandshake(); diff --git a/src/main/java/com/morlunk/jumble/net/JumbleObserver.java b/src/main/java/com/morlunk/jumble/net/JumbleObserver.java index e2c1bae..b95ec11 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleObserver.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleObserver.java @@ -78,7 +78,7 @@ public class JumbleObserver extends IJumbleObserver.Stub { } @Override - public void onUserRemoved(User user) throws RemoteException { + public void onUserRemoved(User user, String reason) throws RemoteException { } diff --git a/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java b/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java index c506146..55697a5 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java @@ -17,7 +17,9 @@ package com.morlunk.jumble.net; import java.io.IOException; +import java.net.Proxy; import java.net.Socket; +import java.net.SocketAddress; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -87,5 +89,4 @@ public class JumbleSSLSocketFactory extends SSLSocketFactory { public Socket createSocket() throws IOException { return mSslContext.getSocketFactory().createSocket(); } - }
\ No newline at end of file diff --git a/src/main/java/com/morlunk/jumble/net/UserHandler.java b/src/main/java/com/morlunk/jumble/net/UserHandler.java index fe557ca..e004a97 100644 --- a/src/main/java/com/morlunk/jumble/net/UserHandler.java +++ b/src/main/java/com/morlunk/jumble/net/UserHandler.java @@ -28,6 +28,8 @@ import com.morlunk.jumble.model.User; import com.morlunk.jumble.protobuf.Mumble; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -36,6 +38,18 @@ import java.util.List; */ public class UserHandler extends JumbleMessageHandler.Stub { + /** + * Comparator to sort users alphabetically. + */ + private Comparator<Integer> mUserComparator = new Comparator<Integer>() { + @Override + public int compare(Integer lhs, Integer rhs) { + User ulhs = mUsers.get(lhs); + User urhs = mUsers.get(rhs); + return ulhs.getName().compareTo(urhs.getName()); + } + }; + private JumbleService mService; private HashMap<Integer, User> mUsers = new HashMap<Integer, User>(); @@ -55,6 +69,14 @@ public class UserHandler extends JumbleMessageHandler.Stub { mUsers.clear(); } + /** + * Sorts the users in the provided channel alphabetically. + * @param channel The channel containing the users to sort. + */ + private void sortUsers(Channel channel) { + Collections.sort(channel.getUsers(), mUserComparator); + } + @Override public void messageUserState(Mumble.UserState msg) { User user = mUsers.get(msg.getSession()); @@ -69,6 +91,7 @@ public class UserHandler extends JumbleMessageHandler.Stub { // Assume in root channel. TODO FIX ME PLEASE OH GOD WHY DOES THIS WORK Channel root = mService.getChannelHandler().getChannel(0); root.addUser(user.getSession()); + sortUsers(root); newUser = true; } else @@ -158,6 +181,7 @@ public class UserHandler extends JumbleMessageHandler.Stub { old.removeUser(user.getSession()); channel.addUser(user.getSession()); + sortUsers(channel); if(!newUser) { mService.notifyObservers(new JumbleService.ObserverRunnable() { @Override @@ -200,7 +224,7 @@ public class UserHandler extends JumbleMessageHandler.Stub { @Override public void messageUserRemove(Mumble.UserRemove msg) { - String reason = msg.getReason(); + final String reason = msg.getReason(); /* * TODO: logging @@ -215,7 +239,7 @@ public class UserHandler extends JumbleMessageHandler.Stub { mService.notifyObservers(new JumbleService.ObserverRunnable() { @Override public void run(IJumbleObserver observer) throws RemoteException { - observer.onUserRemoved(user); + observer.onUserRemoved(user, reason); } }); } |